/* styles2.css — GENERADO por design-lab/css-v2/build.sh el 2026-08-11 22:48 — NO EDITAR A MANO: edita design-lab/css-v2/src/ */
/* ══ 00 · TOKENS ═══════════════════════════════════════════════════════════
   La única fuente de verdad del sistema. Ningún otro archivo declara colores,
   tamaños de fuente, radios, sombras ni z-index en crudo.
   Paleta = canon cost.cl (medida del sitio de marketing).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer reset, tokens, base, layout, componentes, vistas, utilidades, terceros, print;

@layer tokens {
  :root {
    /* ── Marca ──────────────────────────────────────────────────────── */
    --ink:      #02094b;   /* tinta: texto principal */
    --brand:    #173a7b;   /* azul: títulos, acción primaria */
    --accent:   #006bad;   /* azul2: interacción, links, focus */
    --cyan:     #62c2cd;
    --magenta:  #c72f8e;
    --dot:      #ff2436;   /* solo el punto del wordmark */

    /* ── Texto ──────────────────────────────────────────────────────── */
    --ink-2:    #173a7b;
    --muted:    rgba(2, 9, 75, .66);   /* suave */
    --muted-2:  rgba(2, 9, 75, .45);   /* tenue */

    /* ── Superficies ────────────────────────────────────────────────── */
    --bg:        #ffffff;
    --surface:   #ffffff;
    --surface-2: #f2f6fa;   /* banda */
    --surface-3: #e9eff6;

    /* ── Bordes (siempre tinta translúcida) ─────────────────────────── */
    --line-soft:   rgba(2, 9, 75, .07);  /* separadores de fila */
    --line:        rgba(2, 9, 75, .12);  /* borde estándar */
    --line-2:      rgba(2, 9, 75, .2);   /* borde de control */
    --line-strong: rgba(2, 9, 75, .32);

    /* ── Sidebar ────────────────────────────────────────────────────── */
    --nav:   var(--tono-marino);
    --nav-2: var(--tono-marino-2);

    /* Tonos del riel (Ajustes › Apariencia). Todo lo que viste el riel —el
       fondo, sus tres niveles de texto, el ítem activo, la franja superior y
       la pantalla de arranque— se calcula mezclando --surface con estos dos,
       así que retintar el menú es cambiar sólo --nav/--nav-2.
       Los seis están medidos contra su propio fondo: el texto más tenue del
       riel (64% blanco) queda entre 7,2:1 y 7,8:1 —el marino canon da 7,68—
       y el suave (82%) entre 11,1 y 12,3. Todos por encima de AAA. Un tono
       nuevo se agrega aquí y se mide: si sube de luminancia, el riel deja de
       leerse y no lo avisa nadie. */
    --tono-marino:     #02094b;  --tono-marino-2:   #173a7b;
    --tono-grafito:    #141821;  --tono-grafito-2:  #333a4d;
    --tono-petroleo:   #03262b;  --tono-petroleo-2: #0d4d55;
    --tono-bosque:     #062417;  --tono-bosque-2:   #124d33;
    --tono-vino:       #2e0a16;  --tono-vino-2:     #63182f;
    --tono-cafe:       #241610;  --tono-cafe-2:     #4d3225;

    /* ── Semánticos (texto legible sobre claro) + tintes por fórmula ── */
    --green:       #3d9960;
    --green-soft:  color-mix(in srgb, #54b774 12%, #fff);
    --green-vivo:  #54b774;   /* verde canon puro: gráficos, dots, barras */
    --blue:        #006bad;
    --blue-soft:   color-mix(in srgb, #006bad 9%, #fff);
    --amber:       #a1720a;
    --amber-soft:  color-mix(in srgb, #f0b323 16%, #fff);
    --amber-bright: #f0b323;  /* CTA destacado; texto encima: #3a2a00 */
    --amber-cta-ink: #3a2a00;
    --red:         #c2413a;
    --red-soft:    color-mix(in srgb, #c2413a 9%, #fff);
    --red-deep:    #a83630;
    --teal:        #2c8c99;
    --teal-soft:   color-mix(in srgb, #62c2cd 14%, #fff);
    --violet:      #5b4bc4;
    --violet-soft: color-mix(in srgb, #5b4bc4 9%, #fff);
    --rose:        #c72f8e;
    --rose-soft:   color-mix(in srgb, #c72f8e 8%, #fff);

    /* Derivaciones estándar para acentos arbitrarios (usar con var(--c)):
       fondo  color-mix(in srgb, <color> 6%, #fff)
       borde  color-mix(in srgb, <color> 24%, transparent)
       hover  +4 puntos en ambos */

    /* ── Tipografía ─────────────────────────────────────────────────── */
    --serif: Merriweather, Georgia, "Times New Roman", serif;
    --sans: Inter, "Inter Variable", "Segoe UI Variable", ui-sans-serif,
            system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --font-display: var(--serif);                          /* títulos */
    --font-numeric: "Inter Tight", Inter, ui-sans-serif,
                    system-ui, sans-serif;                  /* cifras grandes */

    /* Escala cerrada — únicos font-size permitidos */
    --fs-caption: 11px;   /* labels caja alta, headers de tabla */
    --fs-small:   12px;   /* metadatos, hints */
    --fs-body:    13px;   /* celdas, cuerpo denso */
    --fs-body-lg: 14.5px; /* cuerpo cómodo */
    --fs-h3:      17px;   /* título de panel */
    --fs-h2:      21px;   /* título de sección/modal */
    --fs-title:   29px;   /* título de vista (serif) */

    /* ── Formas ─────────────────────────────────────────────────────── */
    --r-xs: 5px;
    --r-sm: 8px;    /* botones, inputs */
    --r:    10px;   /* tarjetas, tablas */
    --r-lg: 13px;   /* modales, tarjetas grandes */

    /* ── Elevación (tintada con la tinta de marca) ──────────────────── */
    --shadow-xs: 0 1px 2px rgba(2, 9, 75, .05);
    --shadow-sm: 0 6px 16px -8px rgba(2, 9, 75, .18);
    --shadow-md: 0 18px 40px -30px rgba(2, 9, 75, .5);
    --shadow-lg: 0 40px 90px -34px rgba(2, 9, 75, .55);   /* modales */
    --focus: 0 0 0 3px rgba(0, 107, 173, .2);

    /* ── Z — escala completa; prohibido el número suelto ────────────── */
    --z-dropdown:  40;
    --z-sticky:    60;
    --z-popover:   90;
    --z-modal:     100;
    --z-modal-top: 120;
    --z-dialog:    130;
    --z-toast:     160;
    --z-loader:    200;

    /* ── Movimiento ─────────────────────────────────────────────────── */
    --ease:     cubic-bezier(.22, .61, .36, 1);
    --ease-pop: cubic-bezier(.2, .8, .3, 1);
    --t:    .16s var(--ease);
    --t-lg: .26s var(--ease);

    /* ── Densidades y layout ────────────────────────────────────────── */
    --control-h: 40px;
    --control-h-compact: 32px;   /* solo dentro de tablas / barras densas */
    --sidebar-w: 238px;
    /* Alto del cromo que la navegación superior añade encima del lienzo. Con
       el riel lateral no ocupa nada; con la franja arriba son sus 52px. Todos
       los `calc(100dvh - N)` de las vistas lo descuentan, así que los topes de
       tabla siguen cuadrando en los dos modos sin duplicar un solo número.
       Los N de cada vista están MEDIDOS, no estimados: si cambia el aire
       sobre la barra hay que volver a medirlos (al mover el padding superior
       del lienzo a la barra pegajosa, los 22 subieron 16px de golpe). */
    --nav-alto: 0px;
    /* Tope de la línea de lectura. Estaba en 1460 y en un portátil de 16"
       escalado (~2040px de viewport) dejaba 180px muertos a cada lado
       mientras Libro de compras se cortaba 1300px por la derecha: el freno
       estorbaba justo donde más datos hay. 1760 llena esa pantalla entera y
       sigue evitando líneas absurdas en un monitor de 27". Quien quiera
       borde a borde en cualquier monitor pone `none` aquí. */
    --view-max: 1760px;
  }

  /* El tono elegido sólo reapunta los dos tokens del riel; el resto de la
     cascada ya deriva de ellos. "marino" no tiene regla: es el valor por
     defecto de :root y así el atributo ausente y el atributo en marino
     significan lo mismo. */
  :root[data-nav-tono="grafito"]  { --nav: var(--tono-grafito);  --nav-2: var(--tono-grafito-2); }
  :root[data-nav-tono="petroleo"] { --nav: var(--tono-petroleo); --nav-2: var(--tono-petroleo-2); }
  :root[data-nav-tono="bosque"]   { --nav: var(--tono-bosque);   --nav-2: var(--tono-bosque-2); }
  :root[data-nav-tono="vino"]     { --nav: var(--tono-vino);     --nav-2: var(--tono-vino-2); }
  :root[data-nav-tono="cafe"]     { --nav: var(--tono-cafe);     --nav-2: var(--tono-cafe-2); }
}
/* ══ 01 · RESET ════════════════════════════════════════════════════════════
   Mínimo y moderno. El único !important sancionado del sistema vive aquí.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer reset {
  *, *::before, *::after { box-sizing: border-box; }

  /* !important sancionado: [hidden] debe ganar SIEMPRE, incluso a un
     display declarado por un componente (lección v1: display:grid lo pisaba). */
  [hidden] { display: none !important; }

  h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }
  ul[class], ol[class] { margin: 0; padding: 0; list-style: none; }

  img, svg, canvas { display: block; max-width: 100%; }

  button, input, select, textarea { font: inherit; color: inherit; }
  button { cursor: pointer; background: none; border: 0; padding: 0; }

  a { color: inherit; }

  ::selection { background: color-mix(in srgb, var(--accent) 16%, transparent); }

  ::-webkit-scrollbar { width: 6px; height: 6px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--ink) 18%, transparent); border-radius: 999px; }
  ::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--ink) 30%, transparent); }

  /* !important sancionado: es el interruptor global de accesibilidad — debe
     ganarle a cualquier transición/animación que un componente declare. */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: .01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: .01ms !important;
    }
  }
}
/* ══ 02 · BASE ═════════════════════════════════════════════════════════════
   Elementos crudos: documento, jerarquía tipográfica, label, controles de
   formulario, tabla nativa. LA receta de cada elemento vive aquí una sola vez;
   los componentes componen encima, nunca la repiten.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer base {
  html {
    min-height: 100%;
    background: var(--bg);
  }

  body {
    margin: 0;
    min-height: 100vh;
    font-family: var(--sans);
    font-size: var(--fs-body-lg);
    line-height: 1.5;
    font-weight: 400;
    color: var(--ink);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }

  /* Iconos inline: app.js inyecta SVG sin atributos de tamaño ni trazo.
     Sin esta receta el SVG toma 0 × 0 dentro de un flex y las acciones como
     campanita/actualizar quedan como cuadrados vacíos. Los SVG especiales
     pueden ajustar tamaño o color desde su componente. */
  svg {
    flex: none;
    width: 16px;
    height: 16px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  /* ── Jerarquía ──────────────────────────────────────────────────────── */
  h1 { font-size: var(--fs-title); line-height: 1.12; font-weight: 400; }
  h2 { font-size: var(--fs-h3);  line-height: 1.2;  font-weight: 700; }
  h3 { font-size: var(--fs-body-lg); line-height: 1.25; font-weight: 700; }
  h4 { font-size: var(--fs-body); line-height: 1.3; font-weight: 700; }

  small { font-size: var(--fs-small); }

  .eyebrow {
    font-size: var(--fs-caption);
    font-weight: 650;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--muted-2);
  }

  /* ── Label (receta canon §2) ────────────────────────────────────────── */
  label {
    display: grid;
    gap: 6px;
    font-size: var(--fs-caption);
    font-weight: 650;
    color: var(--muted);
    letter-spacing: .07em;
    text-transform: uppercase;
  }

  /* Los labels que envuelven checkbox/radio son texto de corrido */
  label:has(> input[type="checkbox"]),
  label:has(> input[type="radio"]),
  label:has(span input[type="checkbox"]) {
    text-transform: none;
    letter-spacing: 0;
    font-size: var(--fs-small);
    font-weight: 550;
  }

  /* ── Controles ──────────────────────────────────────────────────────── */
  input,
  select,
  textarea {
    width: 100%;
    border: 1px solid var(--line-2);
    border-radius: var(--r-sm);
    color: var(--ink);
    background: var(--surface);
    font-weight: 400;
    box-shadow: var(--shadow-xs);
    transition: border-color var(--t), box-shadow var(--t), background var(--t);
  }

  input,
  select { height: var(--control-h); padding: 0 11px; }

  textarea {
    min-height: 80px;
    padding: 10px 11px;
    resize: vertical;
  }

  input:focus-visible,
  select:focus-visible,
  textarea:focus-visible,
  input:focus,
  select:focus,
  textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--focus), var(--shadow-xs);
  }

  input:disabled,
  select:disabled,
  textarea:disabled {
    background: var(--surface-2);
    color: var(--muted);
    cursor: not-allowed;
    box-shadow: none;
  }

  input[readonly] { background: var(--surface-2); color: var(--muted); }

  ::placeholder { color: var(--muted-2); }

  input[type="checkbox"],
  input[type="radio"] {
    width: auto;
    height: auto;
    box-shadow: none;
    accent-color: var(--accent);
  }

  /* Select: chevron propio, chrome nativo fuera */
  select {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 34px;
    font-weight: 550;
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='13' viewBox='0 0 24 24' fill='none' stroke='%238E90AE' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
  }
  select:hover:not(:disabled) { border-color: var(--line-strong); }

  button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--r-xs);
  }

  button:disabled { cursor: not-allowed; opacity: .45; }

  /* ── Tabla nativa: LA receta (canon §5) ─────────────────────────────── */
  table {
    width: 100%;
    border-collapse: collapse;
    background: var(--surface);
  }

  th {
    background: var(--surface-2);
    color: var(--muted-2);
    border-bottom: 1px solid var(--line);
    text-align: left;
    font-size: var(--fs-caption);
    padding: 9px 11px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    white-space: nowrap;
    position: sticky;
    top: 0;
    z-index: 1;
  }

  td {
    border-bottom: 1px solid var(--line-soft);
    padding: 10px 11px;
    vertical-align: middle;
    font-size: var(--fs-body);
  }

  tbody tr:hover td { background: var(--surface-2); }
  tr:last-child td { border-bottom: 0; }

  /* Cifras: siempre tabulares, en tabla y fuera de ella */
  td, th, .amount, .money { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

  td.empty {
    color: var(--muted-2);
    text-align: center;
    padding: 26px;
    font-weight: 550;
  }

  hr { border: 0; border-top: 1px solid var(--line); }

  code { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: var(--fs-small); }
}
/* ══ 03 · LAYOUT — esqueleto de la app ══════════════════════════════════════
   Cubre: .app-shell · .sidebar (riel completo: .brand, .brand-mark(--viasanto),
   #appLogo, .nav-search/#navSearchInput, .nav-list(--reordering, .is-searching),
   .nav-group(-toggle/-plus/-body), .nav-item(--dragging, .active),
   .nav-subsection-label, .nav-search-miss/-empty, .sidebar-foot,
   .connection-state/-row/-percent/-progress/-task*(--done), .status-dot,
   .user-state, .user-identity, .sidebar-foot-actions, .sidebar-settings-button)
   · .sidebar-brand (SOLO posición: el wordmark lo pinta el agente login-boot)
   · .workspace · .workspace > .view · .view/.view.active
   Fuente v1: styles.css ~L494-1326 (shell+riel+pie), ~L5026 (.view),
   ~L10303-10361 (responsive 980), ~L15839 (subtítulos), ~L18214-18290
   (buscador + scroll honesto), ~L18379 (ítem suelto) — fusión last-wins.
   Estados: completo — hover · focus-visible · :active · .active · loading ·
   indeterminate · ok/bad · done · searching · dragging. "disabled" no aplica:
   el nav nunca deshabilita ítems, los oculta con [hidden] (reset).
   Porte (componente de mayor superficie del sistema — declaraciones agrupadas
   por función en vez de una por línea para caber en presupuesto): el shimmer
   que v1 barría sobre la barra al cargar muere (redundante con .indeterminate,
   que ya es el aviso); el "+" del grupo usa inset:0;margin:auto en vez de
   translate; el vaivén de reordenar ya no alterna delay por ítem; tarea
   terminada (--done) solo aquieta el pulso en verde, sin recolorear texto.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer layout {

  /* ── Shell: riel fijo + lienzo fluido ─────────────────────────────────── */
  /* Alto clavado al viewport, no `min-height`: lo que scrollea es el lienzo,
     no la página. Con la página scrolleando, el `sticky` de la barra de vista
     no servía de nada —se resuelve contra el lienzo, que nunca se movía— y
     tanto la franja como la cápsula se iban hacia arriba con el contenido.
     Cromo quieto, contenido en movimiento: es lo que hace que el vidrio tenga
     algo que refractar y lo que mantiene los indicadores siempre a mano. */
  .app-shell {
    height: 100dvh;
    display: grid;
    grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  }

  /* ── Riel: navy; su "tinta" es blanco (var(--surface)) atenuado.
     Custom properties locales — derivaciones de tokens, no tokens nuevos.
     Las comparte con .topbar y .tb-panel: es la misma superficie navy, solo
     que tumbada. Declararlas una vez evita que los dos modos se separen. */
  .sidebar,
  .topbar,
  .tb-panel {
    --rail-ink:    color-mix(in srgb, var(--surface) 96%, var(--nav));
    --rail-soft:   color-mix(in srgb, var(--surface) 82%, var(--nav));
    --rail-muted:  color-mix(in srgb, var(--surface) 64%, var(--nav));
    --rail-veil:   color-mix(in srgb, var(--surface) 4%, transparent);
    --rail-veil-2: color-mix(in srgb, var(--surface) 7%, transparent);
    --rail-veil-3: color-mix(in srgb, var(--surface) 12%, transparent);
    --rail-line:   color-mix(in srgb, var(--surface) 8%, transparent);
    --rail-line-2: color-mix(in srgb, var(--surface) 16%, transparent);

  }

  .sidebar {
    position: sticky; top: 0; height: 100dvh;
    display: flex; flex-direction: column; gap: 16px; padding: 16px 10px;
    background: linear-gradient(180deg, var(--rail-veil), transparent 42%), var(--nav);
    border-right: 1px solid var(--rail-line);
    color: var(--rail-soft);
    overflow: hidden;   /* el único scroll del riel vive en .nav-list */
  }
  /* Foco sobre navy: el aro accent de la base se pierde; el riel lo aclara */
  .sidebar button:focus-visible,
  .topbar button:focus-visible,
  .tb-panel button:focus-visible { outline-color: var(--cyan); }

  /* ── Marca del tenant (logo cuadrado; apaisado vía .is-wide-logo de JS) ── */
  .brand { flex: none; display: flex; align-items: center; gap: 12px; padding: 4px 8px 10px; }
  .brand-mark {
    flex: none; width: 40px; height: 40px;
    display: grid; place-items: center;
    color: var(--surface);
    font-size: var(--fs-body); font-weight: 800;   /* iniciales de respaldo si no hay logo */
  }
  .brand-mark--viasanto { overflow: visible; padding: 0; }
  .brand-mark img { width: 100%; height: 100%; object-fit: contain; }   /* #appLogo */

  /* Logo rectangular: ocupa el ancho del riel completo */
  .brand.is-wide-logo { flex-direction: column; align-items: stretch; gap: 8px; }
  .brand.is-wide-logo .brand-mark { width: 100%; height: auto; max-height: 76px; display: block; }
  .brand.is-wide-logo .brand-mark img { height: auto; max-height: 76px; }

  /* ── Buscador de vistas ──
     Pisa la receta clara de 02-base: sobre navy, translúcido y derivado de
     currentColor. Vacío = placeholder; disabled no aplica (nunca se apaga). */
  .nav-search { flex: none; margin: 2px 0 6px; }
  .nav-search input {
    height: var(--control-h-compact); padding: 0 10px;
    border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
    border-radius: var(--r);
    background: color-mix(in srgb, currentColor 8%, transparent);
    color: inherit; font-size: var(--fs-body); font-weight: 550;
    box-shadow: none;
  }
  .nav-search input::placeholder { color: color-mix(in srgb, currentColor 48%, transparent); }
  /* El foco se marca en borde+fondo: el aro azul de la base es invisible aquí */
  .nav-search input:focus,
  .nav-search input:focus-visible {
    outline: none; box-shadow: none;
    border-color: color-mix(in srgb, currentColor 42%, transparent);
    background: color-mix(in srgb, currentColor 12%, transparent);
  }
  .nav-search input::-webkit-search-cancel-button { -webkit-appearance: none; }

  /* ── Lista de navegación: scroll honesto con barra fina visible ───────── */
  .nav-list {
    flex: 1 1 auto; min-height: 0;
    display: grid; align-content: start; gap: 6px;
    position: relative;
    overflow-x: hidden; overflow-y: auto;
    padding: 2px 4px 10px 0;
    margin-right: -4px;   /* la barra corre pegada al borde del riel */
    scrollbar-width: thin;
    scrollbar-color: color-mix(in srgb, currentColor 30%, transparent) transparent;
  }
  .nav-list::-webkit-scrollbar-thumb { background: color-mix(in srgb, currentColor 25%, transparent); }
  .nav-list:hover::-webkit-scrollbar-thumb { background: color-mix(in srgb, currentColor 40%, transparent); }
  .nav-list > .nav-item { flex: none; }   /* ítem suelto de primer nivel (Taller): no se estira */

  /* ── Grupos colapsables ───────────────────────────────────────────────── */
  .nav-group { display: grid; gap: 4px; min-width: 0; }
  .nav-group-toggle {
    width: 100%; min-height: var(--control-h-compact);
    padding: 0 8px 0 10px;
    display: grid; grid-template-columns: minmax(0, 1fr) 22px; align-items: center; gap: 8px;
    border: 1px solid var(--rail-line); border-radius: var(--r-sm);
    background: var(--rail-veil); color: var(--rail-ink);
    text-align: left;
    font-size: var(--fs-caption); font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
    transition: background var(--t), border-color var(--t), transform var(--t);
  }
  .nav-group-toggle:hover { background: var(--rail-veil-2); border-color: var(--rail-line-2); }
  .nav-group-toggle:active { transform: translateY(1px); }

  /* El "+" del grupo: gira a "−" al abrir (data-collapsed lo escribe JS) */
  .nav-group-plus {
    position: relative; width: 22px; height: 22px; border-radius: 50%;
    background: var(--rail-veil-2);
    transition: background var(--t), transform var(--t-lg);
  }
  .nav-group-toggle:hover .nav-group-plus { background: var(--rail-veil-3); }
  .nav-group-plus::before,
  .nav-group-plus::after {
    content: ""; position: absolute; inset: 0; margin: auto;
    width: 10px; height: 2px; border-radius: 999px;
    background: currentColor;
    transition: transform var(--t-lg), opacity var(--t);
  }
  .nav-group-plus::after { transform: rotate(90deg); }
  .nav-group[data-collapsed="false"] .nav-group-plus { transform: rotate(180deg); }
  .nav-group[data-collapsed="false"] .nav-group-plus::after { opacity: 0; transform: rotate(0deg); }

  .nav-group-body {
    display: grid; gap: 4px; overflow: hidden;
    max-height: 720px;   /* tope holgado > grupo más alto; v1 recortaba en 480 */
    opacity: 1; transform-origin: top;
    transition: max-height var(--t-lg), opacity var(--t), transform var(--t-lg);
  }
  .nav-group[data-collapsed="true"] .nav-group-body {
    max-height: 0; opacity: 0; pointer-events: none;
    transform: translateY(-4px);
  }

  /* ── Ítem de navegación ──
     Estados: reposo · hover · :active (agarre) · .active (vista actual) ·
     focus-visible (aro cyan del riel) · [hidden] (sin permiso, reset) ·
     --dragging. Loading no aplica: el cambio de vista pinta en .view. */
  .nav-item {
    width: 100%; min-height: 36px; padding: 0 10px;
    display: flex; align-items: center; gap: 8px;
    border: 1px solid transparent; border-radius: var(--r);
    background: transparent; color: var(--rail-muted);
    text-align: left; font-size: var(--fs-body); font-weight: 550;
    transition: background var(--t), color var(--t), border-color var(--t);
    user-select: none; touch-action: manipulation;
    cursor: grab;   /* los ítems se reordenan arrastrando */
  }
  .nav-item:active { cursor: grabbing; }
  .nav-item svg { width: 16px; height: 16px; opacity: .7; transition: opacity var(--t); }
  .nav-item:hover { background: var(--rail-veil-2); color: var(--rail-ink); }
  .nav-item:hover svg { opacity: .9; }
  /* Vista actual: tarjeta blanca sobre el navy */
  .nav-item.active {
    background: var(--surface); color: var(--ink); font-weight: 700;
    border-color: var(--rail-line-2); box-shadow: var(--shadow-xs);
  }
  .nav-item.active svg { opacity: 1; }

  /* Subtítulo dentro de un grupo (Finanzas). 11px = piso de la escala (v1
     usaba 8.5px, fuera de escala). La sangría alinea con el texto del ítem. */
  .nav-subsection-label {
    display: block; padding: 12px 8px 4px 32px;
    color: color-mix(in srgb, var(--surface) 45%, var(--nav));
    font-size: var(--fs-caption); font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
  }

  /* Reordenamiento por arrastre (estadoJS: --reordering / --dragging) */
  .nav-list--reordering { cursor: grabbing; }
  .nav-list--reordering .nav-item:not(.nav-item--dragging):not([hidden]) {
    animation: sidebarWiggle .17s ease-in-out infinite alternate;
  }
  .nav-item--dragging {
    position: relative; z-index: var(--z-dropdown);   /* solo compite dentro del riel */
    cursor: grabbing; animation: none;
    transform: scale(1.025); box-shadow: var(--shadow-sm);
  }
  @keyframes sidebarWiggle { from { transform: rotate(-.6deg); } to { transform: rotate(.6deg); } }

  /* Buscando (estadoJS .is-searching): grupos abiertos a la fuerza, sin
     subtítulos, solo coincidencias. 3 niveles con razón: estado del padre. */
  .nav-list.is-searching .nav-group[data-collapsed="true"] .nav-group-body {
    display: block; max-height: none; opacity: 1; pointer-events: auto; transform: none;
  }
  .nav-list.is-searching .nav-subsection-label { display: none; }
  .nav-item.nav-search-miss { display: none; }
  .nav-group.nav-search-empty { display: none; }

  /* ── Pie del riel: conexión + identidad ───────────────────────────────── */
  .sidebar-foot {
    display: grid; gap: 8px; flex: none;
    padding: 10px;
    border: 1px solid var(--rail-line); border-radius: var(--r-sm);
    background: linear-gradient(180deg, var(--rail-veil-2), var(--rail-veil));
    color: var(--rail-soft); font-size: var(--fs-small); font-weight: 550;
  }

  /* Bloque conexión — "Base conectada".
     Estados JS: .loading · .indeterminate · .has-tasks; dot: .ok · .bad. */
  .connection-state { --connection-progress: 0%; position: relative; display: grid; gap: 6px; min-width: 0; }
  .connection-row {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 8px;
    min-width: 0;
  }
  #connectionLabel {
    color: var(--rail-ink);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;   /* texto largo: ellipsis, no crece */
  }
  .connection-percent {
    color: var(--rail-ink); font-size: var(--fs-caption); font-weight: 700;
    font-variant-numeric: tabular-nums;   /* muestra "…" en indeterminado */
  }

  /* Dot de estado (vive solo en la fila de conexión — verificado app.js) */
  .status-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--amber-bright); }
  .status-dot.ok { background: var(--green-vivo); box-shadow: 0 0 0 2px color-mix(in srgb, var(--green-vivo) 20%, transparent); }
  .status-dot.bad { background: var(--red); }

  /* Barra de progreso: degradado de marca verde→cyan→azul */
  .connection-progress {
    position: relative; height: 5px; overflow: hidden; border-radius: 999px;
    background: color-mix(in srgb, var(--surface) 14%, transparent);
  }
  .connection-progress span {
    position: absolute; inset: 0 auto 0 0; width: var(--connection-progress); border-radius: inherit;
    background: linear-gradient(90deg, var(--green-vivo), var(--cyan) 58%, var(--blue));
    box-shadow: 0 0 14px color-mix(in srgb, var(--cyan) 35%, transparent);
    transition: width var(--t-lg);
  }
  .connection-state.indeterminate .connection-progress span {
    width: 44%;
    animation: connection-indeterminate 1.1s ease-in-out infinite;
  }

  /* Panel de procesos: flota sobre el pie mientras hay tareas.
     [hidden] (reset) lo saca del árbol cuando la lista queda vacía. */
  .connection-task-panel {
    position: absolute; left: -1px; right: -1px; bottom: calc(100% + 8px);
    max-height: 190px; overflow: hidden; padding: 10px;
    border: 1px solid var(--rail-line-2); border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--nav-2) 45%, var(--nav));
    box-shadow: var(--shadow-md);
    opacity: 0; pointer-events: none;
    transform: translateY(8px) scaleY(.94); transform-origin: bottom;
    transition: opacity var(--t), transform var(--t-lg);
  }
  .connection-state.loading .connection-task-panel,
  .connection-state.has-tasks .connection-task-panel { opacity: 1; pointer-events: auto; transform: none; }

  .connection-task-head {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    margin-bottom: 8px; color: var(--rail-soft);
    font-size: var(--fs-caption); font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  }
  .connection-task-head strong {   /* contador: pastilla cyan clara */
    min-width: 20px; height: 20px;
    display: inline-grid; place-items: center; border-radius: 999px;
    color: var(--nav); background: color-mix(in srgb, var(--cyan) 55%, var(--surface));
    font-size: var(--fs-caption);
  }
  .connection-task-list { display: grid; gap: 6px; }   /* list-style: lo quita el reset */

  .connection-task {
    display: grid; grid-template-columns: 10px minmax(0, 1fr) auto; align-items: start; gap: 8px;
    min-width: 0; color: var(--rail-ink);
    animation: connection-task-rise var(--t-lg) both;
  }
  .connection-task-pulse {
    width: 8px; height: 8px; margin-top: 4px; border-radius: 50%;
    background: var(--cyan);
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--cyan) 35%, transparent);
    animation: connection-task-pulse 1.05s ease-out infinite;
  }
  /* Texto de tarea: tres líneas, todas con ellipsis (rutas y nombres largos) */
  .connection-task strong,
  .connection-task small,
  .connection-task em {
    display: block; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .connection-task strong { font-size: var(--fs-small); font-weight: 700; }
  .connection-task small { margin-top: 2px; color: var(--rail-muted); font-size: var(--fs-caption); font-weight: 650; }
  .connection-task em {   /* contexto: mismo cuerpo que small, atenuado */
    margin-top: 2px; color: var(--rail-soft); font-style: normal;
    font-size: var(--fs-caption); font-weight: 650; opacity: .78;
  }
  .connection-task time {
    align-self: start; padding: 2px 6px; border: 1px solid var(--rail-line-2); border-radius: 999px;
    color: color-mix(in srgb, var(--cyan) 60%, var(--surface));
    background: color-mix(in srgb, var(--cyan) 12%, transparent);
    font-size: var(--fs-caption); font-weight: 700; font-variant-numeric: tabular-nums;
  }
  /* Tarea terminada (JS compone connection-task--${status}): pulso quieto y verde */
  .connection-task--done .connection-task-pulse { background: var(--green-vivo); animation: none; box-shadow: none; }

  @keyframes connection-task-rise { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
  @keyframes connection-task-pulse { to { box-shadow: 0 0 0 8px transparent; } }
  @keyframes connection-indeterminate {
    0%   { left: -48%; width: 42%; }
    50%  { left: 34%;  width: 52%; }
    100% { left: 106%; width: 42%; }
  }

  /* Identidad + acciones */
  .user-state {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 10px;
    min-width: 0; padding-top: 8px;
    border-top: 1px solid var(--rail-line);
  }
  .user-identity {
    display: grid; grid-template-columns: 16px minmax(0, 1fr); align-items: center; gap: 6px;
    min-width: 0; min-height: var(--control-h-compact); padding: 0 10px;
    border: 1px solid var(--rail-line); border-radius: var(--r-lg);
    background: var(--rail-veil); color: var(--rail-ink);
  }
  .user-identity svg { width: 16px; height: 16px; color: var(--rail-muted); }
  .user-identity strong {
    min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;   /* nombre largo: ellipsis; vacío = "—" (JS) */
    font-size: var(--fs-small); font-weight: 700;
  }

  /* Engranaje + "Salir": pastilla segmentada */
  .sidebar-foot-actions { display: inline-flex; align-items: center; flex: none; min-height: var(--control-h-compact); }
  .sidebar-settings-button {
    flex: none; width: 36px; height: var(--control-h-compact); padding: 0;
    display: grid; place-items: center;
    border: 1px solid var(--rail-line); border-radius: var(--r-lg) 0 0 var(--r-lg);
    background: var(--rail-veil); color: var(--rail-ink);
    transition: background var(--t), border-color var(--t), color var(--t);
  }
  .sidebar-settings-button:hover,
  .sidebar-settings-button.active { background: var(--rail-veil-3); border-color: var(--rail-line-2); color: var(--surface); }
  .sidebar-settings-button svg { width: 16px; height: 16px; }
  .sidebar-settings-button span { display: grid; place-items: center; width: 16px; height: 16px; line-height: 0; }

  /* "Salir": variante de riel del .table-action (el componente base vive en
     04-componentes; aquí solo se adapta al contexto oscuro y a la pastilla) */
  .sidebar-foot-actions .table-action {
    min-height: var(--control-h-compact); padding: 0 14px;
    border-color: var(--rail-line-2); border-left: 0; border-radius: 0 var(--r-lg) var(--r-lg) 0;
    background: var(--rail-veil); color: var(--rail-ink);
    font-size: var(--fs-small); font-weight: 700;
  }
  .sidebar-foot-actions .table-action:hover:not(:disabled) { background: var(--rail-veil-3); color: var(--surface); }

  /* ── Wordmark "cost · cl": SOLO posición + tinta del contexto ──
     Los internos (.brandmark, .bm-*) los pinta el agente login-boot. */
  .sidebar-brand {
    display: flex; justify-content: center; padding: 12px 12px 4px;
    color: color-mix(in srgb, var(--surface) 42%, transparent);
  }

  /* ── Lienzo ───────────────────────────────────────────────────────────── */
  /* El 48 de abajo venía de v1: con las tablas ancladas al viewport esa
     franja queda siempre en blanco al pie. 28 basta como respiro.

     `overflow-x: auto` se queda —lo quité un rato y a 1024px desbordaban las
     29 vistas—, pero hay que saber lo que arrastra: cualquier overflow
     distinto de `visible` convierte el lienzo en CONTENEDOR DE SCROLL (el eje
     contrario pasa a `auto` por especificación). Por eso la barra pegajosa se
     ancla a él y no al viewport, y su `top` se mide desde el borde del
     lienzo —que ya empieza bajo la franja—: contarla ahí otra vez daba 90px
     de hueco y la cápsula montada sobre la tarjeta. */
  /* Sin padding ARRIBA: ese aire lo pone la barra pegajosa con su `top`, y
     así el hueco bajo el nav es el mismo quieto que scrolleando (si el
     padding lo pusiera el lienzo, al pegarse la cápsula se subiría hasta
     tocar la franja). El resto de los lados sí van aquí. */
  .workspace { min-width: 0; padding: 0 28px 28px; overflow: auto; }
  .workspace > .view { max-width: var(--view-max); margin-inline: auto; }   /* la línea de lectura no se estira */

  /* Conmutación de vistas (JS marca .active); cada vista ancla sus popovers */
  .view { display: none; }
  .view.active { display: block; position: relative; }

  /* ── Responsive ──
     v1 solo colapsaba el shell en 980px; consolidado a los 3 breakpoints:
     1100 afina densidades, 900 colapsa el riel a barra, 680 compacta. */
  @media (max-width: 1100px) {
    .app-shell { --sidebar-w: 216px; }
    .workspace { padding: 0 24px 40px; }
  }

  @media (max-width: 900px) {
    .app-shell { grid-template-columns: 1fr; }
    /* El riel pasa a barra superior sticky con scroll horizontal */
    .sidebar {
      position: sticky; height: auto; min-height: 64px; z-index: var(--z-sticky);
      flex-direction: row; align-items: center; gap: 10px; padding: 10px 12px;
      overflow: auto;
    }
    .brand { min-width: 170px; padding: 0; }
    .nav-search { width: 160px; margin: 0; }
    .nav-list {
      flex: none; display: flex; align-items: flex-start; gap: 6px;
      overflow: visible; padding: 0; margin-right: 0;
    }
    .nav-group { flex: none; min-width: 140px; }
    .nav-group-body { display: flex; gap: 2px; }
    .nav-group[data-collapsed="true"] { min-width: auto; }
    .nav-group[data-collapsed="true"] .nav-group-body { display: none; }   /* en fila no hay altura que animar */
    .nav-group-toggle { min-height: 36px; white-space: nowrap; }
    .nav-item { width: auto; white-space: nowrap; }
    /* El pie y el wordmark no caben en la barra */
    .sidebar-foot,
    .sidebar-brand { display: none; }
    .workspace { padding: 0 16px 40px; }
  }

  @media (max-width: 680px) {
    .sidebar { min-height: 56px; padding: 8px 10px; }
    .brand { min-width: auto; }
    /* El wordmark apaisado ocupaba ~282px y, sumado al buscador, dejaba
       todos los destinos fuera del primer viewport de un teléfono. En la
       barra móvil se muestra compacto y el buscador cede su lugar al nav. */
    .brand.is-wide-logo { width: 140px; flex: 0 0 140px; }
    .brand.is-wide-logo .brand-mark { width: 140px; max-height: 40px; }
    .brand.is-wide-logo .brand-mark img { max-height: 40px; }
    .nav-search { display: none; }
    .workspace { padding: 0 12px 32px; }
  }
}

/* líneas: ~390 de presupuesto 400 (sidebar: mayor superficie del sistema —
   riel + grupos + drag + conexión con panel de tareas; declaraciones
   agrupadas por función, no una por línea, para caber en presupuesto) */
/* ══ badge — píldoras de estado, chips y luces ══════════════════════════════
   Cubre: .pill(.warn/.tiny), .credit-badge(.warn/.danger/.credit), .expense-
     badge(.ops/.services/.banking/.other), .ar-status-pill(--paid/
     --pending/--partial/--in_review/--cancelled), .reco-badge(.ok/.warn/.bad/
     .info/.muted), .ar-rule-chip, .menu-class-pill(--high/--low/--uncosted/
     --excluded), .menu-quadrant-badge(--star/--volume/--puzzle/--drain/
     --uncosted/--excluded), .menu-excluded-badge, .coffee-event-status(+11
     variantes), .coffee-company-status, .coffee-calendar-task-chip,
     .coffee-status-light(--ok/--watch/--stale), .finance-status-badge(.is-*),
     .bank-row-status(.is-*), .order-status(+9 variantes), .status-chip,
     .user-role(.admin), .user-status(.ok/.off), .inv-temp-badge(+4),
     .inv-unit-pill, .inv-src-badge(+factura/historico/manual/none),
     .annex-badge(.own/.manual/.excel), .annex-status, .mom-preview-badge
     (.is-food/.is-other), .mom-rule-chip, .mom-rule-gastro(.is-food/
     .is-other), .promotion-type-pill(+4), .rule-risk(--ok/--warn/--bad),
     .coffee-quote-type(+variantes)/.coffee-quote-tags,
     .menu-exclusion-chip, .promo-rec-badge(.badge-push/
     combo/alert/valley), .promo-cat-chip(.active), .promo-excluded-chip,
     .status-dot(.ok/.bad), .reconciliation-shift-status
   Fuente v1: styles.css ~L1300, 2505-2600, 2767-2805, 3020-3045, 3993-4030,
     5281-5315, 5777-5900, 7417-7460, 7891-7955, 8860, 9628-9645, 11731-11766,
     12196-12345, 12690-12715, 13872-13998 (+mom-rule-gastro: solo la
     etiqueta, el switch <input> es de campo.css), 14421-14440, 14510,
     14631-14652, 14770-14795, 14969+15361+15718 (promo-rec, 3 gen),
     15775-15785, 17235-17238 + 17746-17785 (v150 dot+texto). Fusionadas
     last-wins.
   Estados: las píldoras son SOLO LECTURA (muestran estado, no lo cambian):
     sin hover/focus/active/disabled. Los chips interactivos (.ar-rule-chip,
     .mom-rule-chip, .mom-rule-gastro, .promo-cat-chip, .promo-excluded-chip)
     traen hover + estado activo; focus-visible y disabled los hereda su
     <button>/<label> de 02-base. Loading/error/vacío no aplican: badge sin
     dato no se pinta.
   NO cubre: .notif-badge — misma clase pero con animaciones ligadas a
     .notif-bell; vive completa en feedback.css (no duplicar aquí).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  .doc-type { font-weight: 700; }

  /* ── LA receta: una sola píldora para todas las familias ──────────────
     Cada variante solo cambia --badge-c; fondo y borde derivan por fórmula
     canon (tinte 8% + borde 24%). v1 traía pesos 680-800 y cuerpos
     9.5-11.5px → unificado a 11px/650. */
  .pill, .credit-badge, .expense-badge, .ar-status-pill, .reco-badge,
  .menu-class-pill, .menu-quadrant-badge, .menu-excluded-badge,
  .coffee-event-status, .coffee-company-status, .coffee-calendar-task-chip,
  .order-status, .status-chip, .user-role, .user-status,
  .inv-unit-pill, .inv-src-badge, .annex-badge, .mom-preview-badge,
  .promotion-type-pill {
    --badge-c: var(--muted);
    display: inline-flex;
    align-items: center;
    /* Indivisible: dentro de un contenedor flex (tarjetas de oportunidad,
       filas de estado) un badge nunca debe encogerse — se le cortaba el
       rótulo. Su texto ya es corto y fijo. */
    flex: none;
    gap: 5px;
    min-height: 21px;
    padding: 2px 8px;
    border: 1px solid color-mix(in srgb, var(--badge-c) 24%, transparent);
    /* Rectángulo de esquina blanda, no cápsula. El 999px convertía cada
       etiqueta de una tabla en un óvalo, y una columna entera de óvalos
       compite con la cifra que se quiere leer. */
    border-radius: var(--r-xs);
    background: color-mix(in srgb, var(--badge-c) 8%, var(--surface));
    color: var(--badge-c);
    font-size: var(--fs-caption);
    font-weight: 650;
    line-height: 1.1;
    white-space: nowrap;
  }

  /* Neutros que leen más fuerte: etiqueta informativa, no estado apagado */
  .pill, .menu-quadrant-badge, .coffee-event-status, .coffee-company-status,
  .inv-unit-pill, .promotion-type-pill { --badge-c: var(--ink-2); }

  /* ── Variantes de color (solo cambian --badge-c) ────────────────────── */

  /* Verde: cerrado, pagado, activo, bien */
  .expense-badge.ops, .ar-status-pill--paid, .reco-badge.ok,
  .menu-class-pill--high, .menu-quadrant-badge--star,
  .coffee-event-status--delivered, .coffee-event-status--paid,
  .order-status--confirmed, .order-status--received,
  .order-status--prepared, .order-status--fulfilled,
  .status-chip, .user-role.admin, .user-status.ok, .inv-src-factura,
  .annex-badge.own, .mom-preview-badge.is-food { --badge-c: var(--green); }

  /* Ámbar: pendiente de trabajo, parcial, atención */
  .pill.warn, .credit-badge.warn, .expense-badge.banking,
  .ar-status-pill--partial, .ar-status-pill--in_review, .reco-badge.warn,
  .menu-class-pill--low, .menu-class-pill--excluded,
  .menu-quadrant-badge--volume, .menu-quadrant-badge--excluded,
  .menu-excluded-badge, .coffee-event-status--production,
  .coffee-event-status--review, .coffee-event-status--partial,
  .coffee-event-status--unbilled, .order-status--draft,
  .order-status--submitted, .inv-src-historico, .annex-badge.excel,
  .promotion-type-nxm { --badge-c: var(--amber); }

  /* Rojo: cancelado, vencido, sin costear */
  .credit-badge.danger, .ar-status-pill--cancelled, .reco-badge.bad,
  .menu-class-pill--uncosted, .menu-quadrant-badge--uncosted,
  .coffee-event-status--cancelled,
  .order-status--cancelled { --badge-c: var(--red); }

  /* Azul: informativo, en tránsito, por cobrar */
  .credit-badge.credit, .ar-status-pill--pending, .reco-badge.info,
  .menu-quadrant-badge--puzzle, .coffee-event-status--confirmed,
  .coffee-event-status--receivable, .coffee-event-status--invoiced,
  .coffee-calendar-task-chip, .order-status--sent_manual,
  .order-status--partially_prepared, .inv-src-manual, .annex-badge.manual,
  .promotion-type-descuento_pct { --badge-c: var(--blue); }

  /* Teal / violeta: categorías, no severidad */
  .expense-badge.services, .promotion-type-precio_fijo { --badge-c: var(--teal); }
  .promotion-type-combo { --badge-c: var(--violet); }

  /* Neutro apagado (.credit-badge.neutral, .expense-badge.other,
     .reco-badge.muted, .user-status.off ya heredan --muted por defecto) */
  .menu-quadrant-badge--drain,
  .coffee-event-status--draft { --badge-c: var(--muted); }

  /* ── Particularidades de forma por familia ──────────────────────────── */

  .pill.tiny, .menu-excluded-badge,
  .inv-src-badge { min-height: 18px; padding: 0 6px; }
  .menu-excluded-badge { margin-left: 6px; vertical-align: middle; }

  /* v1: radio 4px y 9.5px → píldora 999 y 11px (cambio visible) */
  .inv-src-badge {
    margin-left: 4px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    vertical-align: middle;
  }

  /* Sin dato de precio: ni tinte ni borde, solo texto tenue */
  .inv-src-none { border-color: transparent; background: transparent; color: var(--muted-2); }

  .mom-preview-badge { font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
  .menu-class-pill { justify-content: center; min-width: 58px; }

  /* Texto largo: se recorta al ancho (v1 tampoco hacía ellipsis aquí) */
  .menu-quadrant-badge { max-width: 190px; overflow: hidden; }

  .inv-unit-pill {
    justify-content: center;
    min-width: 30px;
    max-width: 48px;
    overflow: hidden;
    padding: 0 6px;
  }

  .ar-status-pill { margin-top: 4px; }
  .coffee-company-status { justify-self: start; }

  /* Nota bajo el badge de crédito: proveedor con ellipsis */
  .credit-badge + small {
    display: block;
    margin-top: 2px;
    max-width: 110px;
    overflow: hidden;
    color: var(--muted);
    font-size: var(--fs-caption);
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* ── Estados dot + texto (canon v150): finanzas y banco. Diseño final
     de v1: sin cápsula — el color vive en un punto de 7px, la etiqueta
     es texto sereno. Se respeta tal cual. */
  .finance-status-badge, .bank-row-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--ink-2);
    font-size: var(--fs-caption);
    font-weight: 650;
    white-space: nowrap;
  }

  /* sin estado: punto neutro */
  .finance-status-badge::before, .bank-row-status::before {
    content: ""; flex: 0 0 auto; width: 7px; height: 7px;
    border-radius: 50%; background: var(--line-2);
  }

  .finance-status-badge.is-muted { color: var(--muted); }
  .finance-status-badge.is-green::before,
  .bank-row-status.is-confirmed::before { background: var(--green); }
  .finance-status-badge.is-amber::before,
  .bank-row-status.is-suggested::before { background: var(--amber); }
  .finance-status-badge.is-red::before,
  .bank-row-status.is-pending::before { background: var(--red); }

  /* Recomendación de promo: mismo lenguaje dot + texto. El acento lo da
     la tarjeta (--rec-accent); estas variantes lo fijan para uso suelto. */
  .promo-rec-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--rec-accent, var(--muted));
    font-size: var(--fs-caption);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    white-space: nowrap;
  }

  .promo-rec-badge::before { content: ""; flex: 0 0 auto; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }

  .promo-rec-badge.badge-push   { --rec-accent: var(--green); }
  .promo-rec-badge.badge-combo  { --rec-accent: var(--blue); }
  .promo-rec-badge.badge-alert  { --rec-accent: var(--red); }
  .promo-rec-badge.badge-valley { --rec-accent: var(--amber); }

  /* ── Puntos y luces (sin texto) ─────────────────────────────────────── */

  /* Estado de conexión: ámbar por defecto = indeterminado */
  .status-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--amber); }
  .status-dot.ok {
    background: var(--green-vivo);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--green-vivo) 20%, transparent);
  }
  .status-dot.bad { background: var(--red); }

  /* Semáforo de empresas coffee: punto 10px con halo del mismo color */
  .coffee-status-light {
    --light-c: var(--muted-2);
    flex: none;
    width: 10px;
    height: 10px;
    margin-top: 4px;
    border-radius: 50%;
    background: var(--light-c);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--light-c) 14%, transparent);
  }
  .coffee-status-light--ok    { --light-c: var(--green); }
  .coffee-status-light--watch { --light-c: var(--amber); }
  .coffee-status-light--stale { --light-c: var(--red); }

  /* ── Chips interactivos ─────────────────────────────────────────────── */

  /* Regla de cobranza aprendida: chip clicable con rol · nombre · × */
  .ar-rule-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 28px;
    padding: 2px 10px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--ink);
    font-size: var(--fs-small);
    font-weight: 650;
    cursor: pointer;
    transition: background var(--t), border-color var(--t);
  }
  .ar-rule-chip:hover { border-color: var(--line-2); background: var(--surface); }
  .ar-rule-chip span { color: var(--muted); font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; }
  .ar-rule-chip strong { max-width: 210px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .ar-rule-chip b { color: var(--muted); font-size: var(--fs-body-lg); line-height: 1; }

  /* Valor de regla MoM: chip blanco con botón de quitar */
  .mom-rule-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 6px 2px 10px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface);
    color: var(--ink-2);
    font-size: var(--fs-small);
    font-weight: 650;
    box-shadow: var(--shadow-xs);
  }
  .mom-rule-chip button {
    padding: 2px;
    border: 0;
    border-radius: 999px;
    background: none;
    color: var(--muted-2);
    font-size: var(--fs-body);
    line-height: 1;
    cursor: pointer;
    transition: color var(--t);
  }
  .mom-rule-chip button:hover { color: var(--red); }

  /* Destino food cost de una regla MoM: etiqueta clicable que envuelve un
     switch (el <input> es de campo.css); verde = gastronómico, gris =
     fuera. Sin :hover propio en v1: el feedback es el propio cambio de
     estado is-food/is-other al tocar el switch. */
  .mom-rule-gastro {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--muted);
    font-size: var(--fs-small);
    font-weight: 650;
    cursor: pointer;
    transition: background var(--t), border-color var(--t), color var(--t);
  }
  .mom-rule-gastro.is-food {
    border-color: color-mix(in srgb, var(--green) 26%, transparent);
    background: var(--green-soft);
    color: var(--green);
  }
  .mom-rule-gastro.is-other { background: var(--surface-2); }

  /* Filtro de categoría de promos: conmutable; .active = sólido tinta */
  .promo-cat-chip {
    padding: 4px 12px;
    border: 1px solid var(--line-2);
    border-radius: 999px;
    background: var(--surface);
    color: var(--ink);
    font-size: var(--fs-small);
    font-weight: 650;
    cursor: pointer;
    transition: background var(--t), border-color var(--t), color var(--t);
  }
  .promo-cat-chip small { color: var(--muted); font-weight: 550; }
  .promo-cat-chip:hover { border-color: var(--line-strong); }
  .promo-cat-chip.active { border-color: var(--ink); background: var(--ink); color: var(--surface); }
  .promo-cat-chip.active small { color: color-mix(in srgb, var(--surface) 70%, transparent); }

  /* Producto excluido del mapa: chip punteado, reincorpora al click */
  .promo-excluded-chip {
    padding: 2px 10px;
    border: 1px dashed var(--line-2);
    border-radius: 999px;
    background: transparent;
    color: var(--muted);
    font-size: var(--fs-small);
    font-weight: 550;
    cursor: pointer;
    transition: color var(--t), border-color var(--t);
  }
  .promo-excluded-chip:hover { border-color: var(--line-strong); color: var(--ink); }

  /* Riesgo de regla aprendida: píldora semántica de solo lectura. */
  .rule-risk {
    display: inline-flex; align-items: center; min-height: 24px;
    padding: 4px 9px; border: 1px solid transparent; border-radius: 999px;
    font-size: var(--fs-caption); font-weight: 700; line-height: 1.2;
  }
  .rule-risk--ok { color: var(--green); border-color: color-mix(in srgb, var(--green) 24%, transparent); background: var(--green-soft); }
  .rule-risk--warn { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 24%, transparent); background: var(--amber-soft); }
  .rule-risk--bad { color: var(--red); border-color: color-mix(in srgb, var(--red) 24%, transparent); background: var(--red-soft); }

  /* Etiquetas del cotizador de eventos. */
  .coffee-quote-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
  .coffee-quote-tags span,
  .coffee-quote-type {
    --badge-c: var(--ink-2);
    display: inline-flex; align-items: center;
    padding: 2px 8px; border: 1px solid color-mix(in srgb, var(--badge-c) 22%, transparent);
    border-radius: 999px; background: color-mix(in srgb, var(--badge-c) 7%, var(--surface));
    color: var(--badge-c); font-size: var(--fs-caption); font-weight: 700;
  }
  .coffee-quote-type { margin-bottom: 6px; }
  .coffee-quote-type--existing { --badge-c: var(--green); }
  .coffee-quote-type--new { --badge-c: var(--blue); }
  .coffee-quote-type--savory { --badge-c: var(--amber); }
  .coffee-quote-type--sweet { --badge-c: var(--red); }
  :is(.coffee-quote-type--beverage, .coffee-quote-type--service) { --badge-c: var(--muted); }

  .menu-exclusion-chip {
    display: inline-flex; align-items: center; min-height: 28px;
    padding: 4px 10px; border: 1px solid var(--line); border-radius: 999px;
    background: var(--surface); color: var(--ink-2); font-size: var(--fs-caption);
    font-weight: 650; cursor: pointer;
  }
  .menu-exclusion-chip:hover { border-color: var(--line-strong); background: var(--surface-2); }

  /* ── Placas de estado (más que píldora, menos que panel) ─────────────── */

  /* Temperatura de compra de inventario: cifra + unidad apiladas */
  .inv-temp-badge {
    display: inline-grid;
    gap: 2px;
    justify-items: center;
    min-width: 78px;
    padding: 4px 8px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    line-height: 1.05;
  }
  .inv-temp-badge strong { color: var(--ink); font-size: var(--fs-caption); font-weight: 800; }
  .inv-temp-badge small { margin: 0; color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
  .inv-temp-badge--very_hot, .inv-temp-badge--hot {
    border-color: color-mix(in srgb, var(--red) 26%, transparent);
    background: color-mix(in srgb, var(--red) 7%, var(--surface));
  }
  .inv-temp-badge--cold, .inv-temp-badge--very_cold {
    border-color: color-mix(in srgb, var(--blue) 24%, transparent);
    background: color-mix(in srgb, var(--blue) 6%, var(--surface));
  }

  /* Barra de estado del anexo de catering */
  .annex-status {
    max-width: 520px;
    padding: 8px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
    color: var(--muted);
    font-size: var(--fs-small);
    font-weight: 650;
  }

  /* Tarjeta de estado del turno en conciliación: fondo cálido → frío.
     v1: strong 22px/820 → 21px/800 (escala). */
  .reconciliation-shift-status {
    display: grid;
    grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr);
    gap: 12px;
    padding: 16px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: linear-gradient(135deg, color-mix(in srgb, var(--amber-bright) 4%, var(--surface)), var(--surface-2));
  }
  .reconciliation-shift-status strong {
    display: block;
    margin-top: 10px;
    font-size: var(--fs-h2);
    font-weight: 800;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
  }
  .reconciliation-shift-status p { margin: 8px 0 0; color: var(--muted); font-size: var(--fs-body); }

  @media (max-width: 900px) {
    .reconciliation-shift-status { grid-template-columns: 1fr; }
  }
}

/* líneas: 400 de presupuesto 400 — al límite: 33 familias en una receta */
/* ══ botón ═════════════════════════════════════════════════════════════════
   Cubre: .primary-action (+.danger, .primary-action--cta), .secondary-action
          (+.danger), .icon-button, .table-action (+.danger, .success) y la
          .account-link/.accounting-entry-link/.statement-account-link,
          .price-link/.price-savings/.price-variation, .mini-action,
          variante compacta contextual 32px/12px (bank-batch-review-bar,
          bank-source-drawer, bank-movement-editor-primary, promo-rec-btn,
          hr-sim-result-actions).
   Fuente v1: styles.css ~L1517-1643 (base), ~L6030/8885 (danger/success),
              ~L13469+13552 (v117: lift y altura única), ~L17112/17268/
              17489/17621/17733 (compactos banco), ~L14995/15434/15751
              (promo-rec-btn); hr.css ~L508. Fusión last-wins de todas.
   Estados: los cinco completos — reposo · hover · focus-visible (propio,
            no el de 02-base: estas clases también salen como <a>; de paso
            corrige table-action, que en v1 quedaba sin foco) · active ·
            disabled. Loading no aplica (vive en #refreshBtn, con el header)
            · error se expresa como .danger · vacío no aplica (siempre hay
            rótulo o icono).
   Contenido límite: el rótulo largo envuelve y min-height crece con él;
            el recorte con ellipsis es decisión de cada vista (spans propios).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── Esqueleto común de los cuatro ──────────────────────────────────────
     También se emiten como <a> (portal Kitchen, ZIP de extensión MP):
     de ahí el text-decoration. v1 traía gap 7px y weight 680, fuera de
     escala → 6px y 650. */
  .primary-action,
  .secondary-action,
  .icon-button,
  .table-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    border-radius: var(--r-sm);
    font-size: var(--fs-body);
    font-weight: 650;
    letter-spacing: 0;
    text-decoration: none;
    box-shadow: var(--shadow-xs);
    transition: background var(--t), border-color var(--t),
                box-shadow var(--t), color var(--t), transform var(--t);
  }

  /* Altura única de control (v117): todos alinean en la misma línea base */
  .primary-action,
  .secondary-action,
  .icon-button { min-height: var(--control-h); }

  /* Foco propio (v1 ~L1556/1593/1614; table-action quedaba sin reemplazo,
     foco invisible por teclado — se corrige aquí). No delega en 02-base:
     `button:focus-visible` no alcanza a los <a> con estas clases
     (orders-portal-link, ZIP de la extensión MP). */
  .primary-action:focus-visible,
  .secondary-action:focus-visible,
  .icon-button:focus-visible,
  .table-action:focus-visible {
    outline: none;
    box-shadow: var(--focus);
  }

  /* Al presionar, el lift del hover baja; al deshabilitar, nada flota
     (la base ya pone opacity .45 + cursor not-allowed) */
  .primary-action:active:not(:disabled),
  .secondary-action:active:not(:disabled),
  .icon-button:active:not(:disabled),
  .table-action:active:not(:disabled) {
    transform: translateY(0);
    box-shadow: var(--shadow-xs);
  }

  .primary-action:disabled,
  .secondary-action:disabled,
  .icon-button:disabled,
  .table-action:disabled {
    box-shadow: none;
    transform: none;
  }

  /* ── Primario: azul de marca (canon §4; el verde queda para éxito) ────── */
  .primary-action {
    padding: 0 16px;
    color: var(--surface);   /* blanco sobre azul: no hay token texto-sobre-oscuro */
    background: var(--brand);
    border: 1px solid var(--brand);
  }

  .primary-action:hover:not(:disabled) {
    background: var(--accent);
    border-color: var(--accent);
    box-shadow: var(--shadow-sm);
    transform: translateY(-1px);
  }

  .primary-action.danger {
    background: var(--red);
    border-color: var(--red);
  }

  .primary-action.danger:hover:not(:disabled) {
    background: var(--red-deep);
    border-color: var(--red-deep);
  }

  /* Variante disponible SIN emisor actual en el DOM: CTA ámbar del canon.
     (El único ámbar de v1 era contextual: .hr-third-party-table
     .primary-action; si esa vista lo necesita, que emita esta clase.)
     El hover re-asserta el ámbar porque el hover base pintaría accent. */
  .primary-action--cta {
    color: var(--amber-cta-ink);
    background: var(--amber-bright);
    border-color: var(--amber-bright);
  }

  .primary-action--cta:hover:not(:disabled) {
    background: color-mix(in srgb, var(--amber-bright) 85%, var(--amber));
    border-color: color-mix(in srgb, var(--amber-bright) 85%, var(--amber));
  }

  /* ── Secundario: borde sobre blanco, texto tinta ──────────────────────── */
  .secondary-action {
    padding: 0 14px;
    color: var(--ink-2);
    background: var(--surface);
    border: 1px solid var(--line-2);
  }

  /* Los tres neutros comparten el mismo hover (v1 traía borde #b9c1cc →
     equivalente canon --line-strong; el lift del table-action es v117 ~L13469) */
  .secondary-action:hover:not(:disabled),
  .icon-button:hover:not(:disabled),
  .table-action:hover:not(:disabled) {
    background: var(--surface-2);
    border-color: var(--line-strong);
    box-shadow: var(--shadow-sm);
    transform: translateY(-1px);
  }

  /* ── Icon button: cuadrado 40 (v117 pisó los 38px de la base v1) ──────── */
  .icon-button {
    width: var(--control-h);
    padding: 0;
    color: var(--ink-2);
    background: var(--surface);
    border: 1px solid var(--line-2);
  }

  /* ── Table action: ghost chico para filas y celdas ──────────────────────
     v1 traía 30px y weight 660, fuera de escala → 28px / 650 heredado. */
  .table-action {
    min-height: 28px;
    padding: 0 10px;
    font-size: var(--fs-small);
    color: var(--ink-2);
    background: var(--surface);
    border: 1px solid var(--line);
  }

  /* Danger de los ghost: fórmula estándar de tinte (borde 24%, hover +4)
     en vez de los hex sueltos de v1 (#e0b8b8/#d99393) */
  .secondary-action.danger,
  .table-action.danger {
    color: var(--red);
    border-color: color-mix(in srgb, var(--red) 24%, transparent);
  }

  .secondary-action.danger:hover:not(:disabled),
  .table-action.danger:hover:not(:disabled) {
    background: var(--red-soft);
    border-color: color-mix(in srgb, var(--red) 28%, transparent);
  }

  /* Success (crear tareas / completar): tinte por fórmula; v1 usaba
     rgba(37,111,74,·) que ya no es el verde canon */
  .table-action.success {
    color: var(--green);
    background: color-mix(in srgb, var(--green) 6%, var(--surface));
    border-color: color-mix(in srgb, var(--green) 24%, transparent);
  }

  .table-action.success:hover:not(:disabled) {
    background: color-mix(in srgb, var(--green) 10%, var(--surface));
    border-color: color-mix(in srgb, var(--green) 28%, transparent);
  }

  /* Variante sólida de éxito: envío por WhatsApp y acciones positivas. */
  .primary-action.success,
  .orders-whatsapp-action {
    background: var(--green);
    color: var(--surface);
  }
  .primary-action.success:hover:not(:disabled),
  .orders-whatsapp-action:hover:not(:disabled) {
    background: color-mix(in srgb, var(--green) 88%, var(--ink));
  }

  /* Links de navegación contable: botón semántico sin chrome. */
  .account-link,
  .accounting-entry-link,
  .statement-account-link {
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--accent);
    font: inherit;
    text-align: left;
    cursor: pointer;
    text-decoration: none;
  }
  :is(.account-link, .accounting-entry-link, .statement-account-link):hover { color: var(--brand); text-decoration: underline; }
  :is(.account-link, .accounting-entry-link, .statement-account-link):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .account-link :is(strong, small) { display: block; }
  .account-link small { margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); }
  .accounting-entry-link { font-weight: 700; }
  .statement-account-link {
    margin-left: 6px;
    padding: 2px 6px;
    border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
    border-radius: var(--r-xs);
    font-size: var(--fs-caption);
    vertical-align: 1px;
  }

  /* Cifras navegables del comparador/consumo. El botón conserva la
     alineación numérica de la celda sin sumar chrome innecesario. */
  .price-variation {
    display: inline-flex;
    justify-content: flex-end;
    min-width: 72px;
    font-variant-numeric: tabular-nums;
  }
  .price-link {
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    font-weight: 700;
    text-align: right;
    cursor: pointer;
  }
  .price-link:hover { color: var(--ink); text-decoration: underline; }
  .price-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .price-link.tone-good { color: var(--green); }
  .price-link.tone-bad { color: var(--red); }
  .price-link.tone-warn { color: var(--amber); }
  .price-link.tone-muted { color: var(--muted); }
  .price-savings { font-variant-numeric: tabular-nums; }

  /* Acciones muy densas dentro de celdas Tabulator. */
  .mini-action {
    min-height: 26px;
    padding: 0 8px;
    border: 1px solid var(--line-2);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--ink);
    font-size: var(--fs-caption);
    font-weight: 700;
    cursor: pointer;
    transition: background var(--t), border-color var(--t), color var(--t);
  }
  .mini-action:hover:not(:disabled) { border-color: var(--ink); background: var(--surface-2); }
  .mini-action:focus-visible { outline: none; box-shadow: var(--focus); }
  .mini-action:disabled { cursor: not-allowed; opacity: .45; }
  .mini-action--strong {
    border-color: color-mix(in srgb, var(--green) 38%, var(--line));
    background: var(--green-soft);
    color: var(--green);
  }
  .mini-action--icon { width: 28px; padding: 0; font-size: var(--fs-body-lg); }

  /* Botón “+” embebido en títulos/listas del resumen. */
  .dashboard-inline-plus {
    display: inline-grid;
    place-items: center;
    width: 24px;
    height: 24px;
    margin-left: 6px;
    border: 1px solid var(--line);
    border-radius: 50%;
    background: var(--surface);
    color: var(--muted);
    cursor: pointer;
  }
  .dashboard-inline-plus:hover { border-color: var(--accent); color: var(--accent); }

  /* ── Variante compacta contextual: 32px / 12px ──────────────────────────
     Barras densas y drawers del módulo banco, botón de tarjetas de
     recomendación de promos y acciones de simuladores RRHH. v1 la repetía
     inline en cada contexto con paddings dispares (6px 12px / 0 14px /
     7px 11px); aquí queda UNA sola receta. */
  .bank-batch-review-bar .primary-action,
  .bank-source-drawer .primary-action,
  .bank-movement-editor-primary .primary-action,
  .hr-sim-result-actions .secondary-action,
  .promo-rec-btn {
    min-height: var(--control-h-compact);
    padding: 0 12px;
    font-size: var(--fs-small);
  }

  /* promo-rec-btn, estado final v1 (~L15751): rótulo en una línea, tinta
     plena y sin la flecha decorativa del primer diseño */
  .promo-rec-btn { white-space: nowrap; color: var(--ink); }
  .promo-rec-btn span { display: none; }

  /* El ghost dentro del drawer va sobre banda fría: borde y tinta un punto
     más firmes para no desaparecer (v1 ~L17489; su hover #9aa4b2 se
     normaliza al hover estándar --line-strong) */
  .bank-source-drawer .table-action {
    border-color: var(--line-2);
    color: var(--ink);
  }
}

/* líneas: ~214 de presupuesto 200 (+14: foco propio real en las cuatro
   clases, incluida table-action, que en v1 quedaba sin reemplazo de outline) */
/* ══ campo · patrones de campo compuestos ══════════════════════════════════
   Cubre: .form-grid, .stack-form, .search-box, .mini-input,
          .table-inline-field, .table-inline-number, .ar-input, .row-check,
          .date-range-custom-grid,
          .check-line (+ --switch), .ui-switch, .permission-check,
          .mom-rule-check/.mom-rule-gastro, .coffee-quote-check,
          .benchmark-field (+ --search, --period,
          -label, small), .settings-vocabulary-grid, .mom-fallback-field,
          .has-error
   Fuente v1: styles.css ~L1699-1712, ~L1928-1958, ~L3215-3242, ~L3636-3682,
          ~L5726-5775, ~L6057-6103, ~L13549-13603, ~L13709-13739,
          ~L14110-14177, ~L17821-17883 (fusionadas); hr.css ~L292.
   Estados: completo | sin loading (un campo no carga: el spinner vive en el
          botón que dispara la acción). Error = .has-error; vacío = ::placeholder.
   Notas: .quality-row (cssSinEmisor) verificada MUERTA — cero emisores en
          archivos PHP, JS y HTML del repo, sin composición dinámica — no se porta.
          .field-error no existe en el contrato. La base ya da label,
          controles crudos, focus y chevron: aquí solo se compone encima.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  .field-hint {
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 550;
    line-height: 1.4;
  }
  /* ── Grillas de formulario ──────────────────────────────────────────── */
  .form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px 14px;
    align-items: end; /* controles en una línea aunque un label ocupe dos */
  }

  .stack-form { display: grid; gap: 14px; }

  /* ── Search box: input con lupa ─────────────────────────────────────── */
  .search-box {
    height: var(--control-h);
    min-width: 200px;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 0 12px;
    border: 1px solid var(--line-2);
    border-radius: var(--r);
    background: var(--surface);
    box-shadow: var(--shadow-xs);
    transition: border-color var(--t), box-shadow var(--t), background var(--t);
  }

  .search-box:hover { border-color: var(--line-strong); }

  .search-box:focus-within {
    border-color: var(--accent);
    box-shadow: var(--focus), var(--shadow-xs);
  }

  .search-box > [data-icon] { flex: none; display: inline-flex; color: var(--muted-2); }

  /* El input interior es invisible: la caja pinta borde, foco y sombra. */
  .search-box input {
    flex: 1;
    min-width: 0;
    height: auto;
    border: 0;
    padding: 0;
    background: transparent;
    box-shadow: none;
  }

  .search-box input:focus,
  .search-box input:focus-visible { border: 0; box-shadow: none; }

  .search-box:has(input:disabled) { background: var(--surface-2); box-shadow: none; }

  /* ── Campos compactos (32px): dentro de tablas y barras densas ──────── */
  .mini-input { height: var(--control-h-compact); }

  .table-inline-field {
    height: var(--control-h-compact);
    min-width: 108px;
    padding: 0 8px;
    font-size: var(--fs-body); /* v1: 12.5px → escala */
  }

  /* El chevron de la base se arrima al alto compacto. */
  select.table-inline-field { padding-right: 28px; background-position: right 8px center; }

  .table-inline-number {
    min-width: 74px;
    max-width: 86px;
    text-align: right;
    font-variant-numeric: tabular-nums;
  }

  /* Campo editable de CxC: comparte el chrome y los estados de la base;
     aquí solo se convierte en control compacto apto para una celda. */
  .ar-input {
    width: 100%;
    min-width: 104px;
    height: var(--control-h-compact);
    padding: 0 8px;
    font-size: var(--fs-body);
    font-weight: 550;
    letter-spacing: 0;
  }

  select.ar-input {
    padding-right: 28px;
    background-position: right 8px center;
  }

  /* Checkbox de selección dentro de tablas (CxC y conciliación). */
  .row-check {
    width: 16px;
    height: 16px;
    accent-color: var(--green);
    vertical-align: middle;
  }

  .row-check:disabled { cursor: not-allowed; }

  /* ── Rango de fechas manual (vive en el popover del date-range) ─────── */
  .date-range-custom-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
  }

  .date-range-custom-grid label { min-width: 0; }

  .date-range-custom-grid input {
    min-width: 0;
    font-size: var(--fs-body);
    font-weight: 650; /* la fecha elegida pesa como valor, no como relleno */
  }

  /* ── Check line: checkbox + texto de corrido ────────────────────────── */
  .check-line {
    margin-top: 12px; /* v1: respira respecto al campo anterior del stack */
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .check-line input { flex: none; }

  .check-line:has(input:disabled) { color: var(--muted-2); cursor: not-allowed; }

  /* ── Variante switch iOS: texto a la izquierda, switch a la derecha ─── */
  .check-line--switch {
    flex-direction: row-reverse;
    justify-content: space-between;
    margin-top: 0;
    min-height: 56px;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    color: var(--ink-2);
    font-weight: 700;
  }

  :is(.check-line--switch, .ui-switch, .user-form .check-line, .permission-check) input {
    appearance: none;
    -webkit-appearance: none;
    position: relative;
    margin: 0;
    width: 46px;
    height: 28px;
    border: 1px solid var(--line-2);
    border-radius: 999px;
    /* Pista apagada: gris frío derivado de la tinta (el gris cálido crudo
       de v1 muere aquí). */
    background: color-mix(in srgb, var(--ink) 16%, var(--surface));
    box-shadow: inset 0 1px 2px color-mix(in srgb, var(--ink) 12%, transparent);
    cursor: pointer;
    transition: background var(--t), border-color var(--t);
  }

  :is(.check-line--switch, .ui-switch, .user-form .check-line, .permission-check) input::before {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--surface);
    box-shadow: 0 1px 4px color-mix(in srgb, var(--ink) 30%, transparent);
    transition: transform .18s var(--ease-pop);
  }

  :is(.check-line--switch, .ui-switch, .user-form .check-line, .permission-check) input:checked { background: var(--green); border-color: var(--green); }
  :is(.check-line--switch, .ui-switch, .user-form .check-line, .permission-check) input:checked::before { transform: translateX(18px); }
  :is(.check-line--switch, .ui-switch, .user-form .check-line, .permission-check) input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  :is(.check-line--switch, .ui-switch, .user-form .check-line, .permission-check) input:disabled { opacity: .45; cursor: not-allowed; }

  .ui-switch { display: inline-flex; align-items: center; flex: none; cursor: pointer; }

  /* La lista de permisos usa la misma pista, distribuida a los extremos. */
  .permission-check {
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-height: 42px;
    padding: 8px 10px;
    border: 1px solid var(--line);
    border-radius: var(--r-xs);
    background: var(--surface-2);
    color: var(--ink-2);
    font-size: var(--fs-small);
    font-weight: 650;
  }

  /* Switch compacto dentro de reglas MoM. */
  :is(.mom-rule-check, .mom-rule-gastro) input {
    appearance: none; -webkit-appearance: none; position: relative;
    flex: none; width: 36px; height: 22px; margin: 0;
    border: 1px solid var(--line-2); border-radius: 999px;
    background: color-mix(in srgb, var(--ink) 16%, var(--surface)); cursor: pointer;
    transition: background var(--t), border-color var(--t);
  }
  :is(.mom-rule-check, .mom-rule-gastro) input::before {
    content: ""; position: absolute; top: 2px; left: 2px;
    width: 16px; height: 16px; border-radius: 50%; background: var(--surface);
    box-shadow: 0 1px 4px color-mix(in srgb, var(--ink) 24%, transparent);
    transition: transform .18s var(--ease-pop);
  }
  :is(.mom-rule-check, .mom-rule-gastro) input:checked { background: var(--green); border-color: var(--green); }
  :is(.mom-rule-check, .mom-rule-gastro) input:checked::before { transform: translateX(14px); }
  :is(.mom-rule-check, .mom-rule-gastro) input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

  /* Checkbox del cotizador presentado como control compuesto. */
  .coffee-quote-check {
    min-height: var(--control-h);
    margin-top: 0;
    padding: 8px 10px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface-2);
    color: var(--ink-2);
    font-size: var(--fs-body);
    font-weight: 700;
  }

  /* ── Campo de toolbar de Precios: caption / control / ayuda ─────────── */
  .benchmark-field {
    min-width: 0;
    width: 100%;
    display: grid;
    /* Filas fijas: la toolbar comparte baseline aunque una ayuda sea larga */
    grid-template-rows: 14px 46px minmax(30px, auto);
    align-items: start;
    gap: 6px; /* v1: 5px → escala */
    color: var(--muted);
    font-size: var(--fs-small);
    font-weight: 700; /* v1: 760 */
  }

  .benchmark-field--search { min-width: 230px; }
  .benchmark-field--period { min-width: 190px; }

  .benchmark-field .search-box,
  .benchmark-field .date-range-filter,
  .benchmark-field select { width: 100%; min-width: 0; }

  .benchmark-field .search-box,
  .benchmark-field .date-range-trigger,
  .benchmark-field select { min-height: var(--control-h); }

  .benchmark-field-label { color: var(--muted); }

  .benchmark-field small {
    display: block;
    color: var(--muted-2);
    font-size: var(--fs-caption); /* v1: 10.5px → escala */
    font-weight: 550;
    line-height: 1.2;
    letter-spacing: 0;
    text-transform: none;
  }

  /* ── Ajustes: vocabulario del negocio + fallback MoM ────────────────── */
  .settings-vocabulary-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 14px;
    align-content: start; /* el panel vecino es alto: contenido arriba */
  }

  .settings-vocabulary-grid label { align-content: start; }

  /* La ayuda es una frase: escapa del uppercase que hereda del label. */
  .settings-vocabulary-grid small {
    font-weight: 400;
    color: var(--muted-2);
    letter-spacing: 0;
    text-transform: none;
  }

  .mom-fallback-field { max-width: 280px; margin-bottom: 12px; }

  /* ── Estado de error: JS lo marca en el contenedor del campo ────────── */
  .has-error input,
  .has-error select,
  .has-error textarea {
    border-color: var(--red);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--red) 11%, transparent);
  }

  .has-error > small { color: var(--red); font-weight: 650; }

  /* ── Responsive (v1: 1380px y ~760px → consolidados a 1100/680) ─────── */
  @media (max-width: 1100px) {
    /* La ayuda larga de Precios se oculta; queda en tooltips de cabecera. */
    .benchmark-field small { display: none; }
    .benchmark-field { grid-template-rows: 14px 46px; }
  }

  @media (max-width: 680px) {
    .benchmark-field { grid-template-rows: auto auto; }
    .search-box { min-width: 0; width: 100%; }
  }
}

/* líneas: ~245 de presupuesto 250 */
/* ══ dropzone ═══════════════════════════════════════════════════════════════
   Cubre: .upload-zone (+label, input oculto), .inventory-import-upload,
          .bank-file-picker (+-title/-help, .has-loaded-file/.has-selected-file)
          con .bank-file-control (+-icon/-copy/-action), .settings-logo-picker,
          .orders-import-file, .recipe-yield-file, .hr-document-upload
          (+.hr-upload-icon/.hr-upload-filename), #view-import .catalog-actions
          input[type="file"].
   Fuente v1: styles.css ~L3246-3296, ~L3538-3556, ~L5428-5437, ~L11021-11059,
          ~L11253-11359, ~L14660, ~L16913-17019 (fusionadas); hr.css ~L254-261.
          Se descarta ~L16204-16223: apunta a un input directo dentro de
          .bank-file-picker que el markup actual ya no emite (app.js
          ~L17800: hoy vive oculto dentro de .bank-file-control).
   Estados: reposo · hover · focus-visible · active · disabled (solo
          bank-file-control recibe el atributo; el resto nunca, v1 tampoco
          lo cubría) · vacío (copy fija por JS) · con-archivo
          (.has-loaded-file/.has-selected-file, solo banco). Loading no
          aplica (vive en el botón que envía el form). Error no aplica (sin
          clase en el contrato). is-dragover no existe en JS — no se declara.
   Notas: ZONA = caja centrada, min-height ~128px (upload-zone,
          inventory-import-upload, hr-document-upload) — resetea la receta
          base de label: aquí es contenido, no rótulo. COMPACTA = fila
          ~40px construida (bank-file-picker, settings-logo-picker) o input
          crudo con botón nativo azul marca (orders-import-file,
          recipe-yield-file, catalog-actions), que sí aprovecha esa receta
          base como caption. --source-accent: gancho opcional que la vista
          de conciliación fija por banco; cae a var(--accent).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── Input oculto (construcciones con label/ícono propio) ────────────── */
  .upload-zone input[type="file"],
  .inventory-import-upload input[type="file"],
  .settings-logo-picker input[type="file"],
  .hr-document-upload input[type="file"] {
    position: absolute; inset: 0 auto auto 0; width: 1px; height: 1px;
    overflow: hidden; opacity: 0; pointer-events: none; clip-path: inset(50%);
  }

  /* Foco → contenedor visible. upload-zone/hr: input y label son hermanos
     (input primero) → hermano adyacente. inventory-import-upload y
     settings-logo-picker: el input es hijo de la propia caja → :has(). */
  .upload-zone input[type="file"]:focus-visible + label,
  .hr-document-upload input[type="file"]:focus-visible + label,
  .inventory-import-upload:has(input:focus-visible),
  .settings-logo-picker:has(input:focus-visible) {
    border-color: var(--accent);
    box-shadow: var(--focus);
  }

  /* ── ZONA: caja centrada grande ───────────────────────────────────────── */
  .upload-zone { position: relative; display: grid; gap: 12px; }

  .upload-zone > label,
  .inventory-import-upload,
  .hr-document-upload label {
    display: grid;
    place-items: center;
    gap: 6px;
    min-height: 128px;
    padding: 20px;
    border: 1.5px dashed var(--line-2);
    border-radius: var(--r-lg);
    background: var(--surface-2);
    color: var(--ink-2);
    text-align: center;
    text-transform: none;
    letter-spacing: normal;
    font-weight: 550;
    cursor: pointer;
    transition: background var(--t), border-color var(--t), box-shadow var(--t);
  }

  .upload-zone > label:hover,
  .inventory-import-upload:hover,
  .hr-document-upload label:hover { background: var(--surface); border-color: var(--accent); }

  .upload-zone > label:active,
  .inventory-import-upload:active,
  .hr-document-upload label:active,
  .settings-logo-picker:active { background: var(--surface-3); }

  .upload-zone > label [data-icon] svg,
  .inventory-import-upload [data-icon] svg { width: 28px; height: 28px; margin-bottom: 4px; color: var(--accent); }

  .upload-zone > label strong,
  .inventory-import-upload strong,
  .hr-document-upload strong { font-size: var(--fs-body-lg); font-weight: 650; }

  .upload-zone > label small,
  .inventory-import-upload small,
  .hr-document-upload small { display: block; color: var(--muted); font-weight: 550; }

  /* hr-document-upload: mismo cascarón, ícono insignia (el glifo "↑" no
     luce suelto sin el círculo de fondo que ya traía v1) */
  .hr-document-upload { margin-bottom: 16px; }

  .hr-upload-icon {
    display: grid; place-items: center; width: 48px; height: 48px; margin-bottom: 6px;
    border-radius: 50%; background: var(--accent); color: var(--surface); /* blanco sobre azul: sin token texto-sobre-oscuro */
    font-size: var(--fs-h2); font-weight: 700; line-height: 1;
  }

  .hr-upload-filename { margin-top: 8px; color: var(--accent); font-size: var(--fs-small); font-weight: 700; font-style: normal; }

  /* ── COMPACTA construida: bank-file-picker ────────────────────────────── */
  .bank-file-picker { display: grid; gap: 4px; color: var(--ink-2); text-transform: none; letter-spacing: normal; font-weight: 400; }

  .bank-file-picker-title {
    color: color-mix(in srgb, var(--source-accent, var(--accent)) 82%, var(--ink));
    font-size: var(--fs-caption); font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  }

  .bank-file-control {
    position: relative;
    display: grid;
    grid-template-columns: 28px minmax(0, 1fr) auto;
    align-items: center;
    gap: 8px;
    min-height: var(--control-h);
    padding: 4px 6px;
    border: 1px solid color-mix(in srgb, var(--source-accent, var(--accent)) 36%, var(--line));
    border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--source-accent, var(--accent)) 6%, var(--surface));
    cursor: pointer;
    transition: border-color var(--t), box-shadow var(--t), transform var(--t);
  }

  .bank-file-control:hover { border-color: var(--source-accent, var(--accent)); box-shadow: var(--shadow-xs); transform: translateY(-1px); }
  .bank-file-control:active { transform: translateY(0); }
  .bank-file-control:has(input:focus-visible) { box-shadow: var(--focus); }
  .bank-file-control:has(input:disabled) { cursor: not-allowed; opacity: .45; }

  .bank-file-control input[type="file"] {
    position: absolute; inset: 0; width: 100%; height: 100%;
    margin: 0; padding: 0; border: 0; opacity: 0; cursor: pointer;
  }

  .bank-file-control-icon {
    display: grid; place-items: center; width: 26px; height: 26px;
    border-radius: var(--r-xs); background: var(--source-accent, var(--accent)); color: var(--surface);
    font-size: var(--fs-body); font-weight: 700; transition: transform var(--t);
  }

  .bank-file-control:hover .bank-file-control-icon { transform: translateY(-2px); }

  .bank-file-control-copy { min-width: 0; }
  .bank-file-control-copy strong,
  .bank-file-control-copy small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .bank-file-control-copy strong { color: var(--ink); font-size: var(--fs-caption); }
  .bank-file-control-copy small { margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); }

  .bank-file-control-action {
    padding: 4px 8px; border-radius: var(--r-xs);
    color: var(--source-accent, var(--accent));
    background: color-mix(in srgb, var(--source-accent, var(--accent)) 12%, transparent);
    font-size: var(--fs-caption); font-weight: 800; text-transform: uppercase; white-space: nowrap;
  }

  /* con-archivo: cargado de sesión anterior (dashed) vs recién elegido (ok) */
  .bank-file-picker.has-loaded-file .bank-file-control { border-style: dashed; }
  .bank-file-picker.has-selected-file .bank-file-control { border-color: var(--green); background: color-mix(in srgb, var(--green-soft) 45%, var(--surface)); }
  .bank-file-picker.has-selected-file .bank-file-control-icon { background: var(--green); transform: translateY(-1px) rotate(4deg); }

  .bank-file-picker-help {
    display: block; margin-top: 2px; overflow: hidden;
    color: var(--muted); font-size: var(--fs-caption); font-weight: 550; line-height: 1.25;
    text-overflow: ellipsis; white-space: nowrap;
  }

  /* ── COMPACTA construida: settings-logo-picker ────────────────────────── */
  .settings-logo-picker {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px;
    border: 1px dashed var(--line-2);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    color: var(--ink-2);
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
    cursor: pointer;
    transition: border-color var(--t), background var(--t), box-shadow var(--t);
  }

  .settings-logo-picker:hover { background: var(--surface); border-color: var(--accent); }

  .settings-logo-picker > [data-icon] {
    display: grid; place-items: center; flex: none; width: 34px; height: 34px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); color: var(--accent);
  }

  .settings-logo-picker > [data-icon] svg { width: 17px; height: 17px; }

  .settings-logo-picker strong,
  .settings-logo-picker small { display: block; min-width: 0; }
  .settings-logo-picker strong { color: var(--ink); font-size: var(--fs-body); }
  .settings-logo-picker small {
    margin-top: 2px; overflow: hidden; color: var(--muted); font-size: var(--fs-small); font-weight: 550;
    text-overflow: ellipsis; white-space: nowrap;
  }

  /* ── COMPACTA nativa: input crudo con botón de marca ──────────────────── */
  .orders-import-file input[type="file"],
  .recipe-yield-file input[type="file"],
  #view-import .catalog-actions input[type="file"] { min-height: var(--control-h); padding: 4px; cursor: pointer; }

  .orders-import-file input[type="file"]::file-selector-button,
  .recipe-yield-file input[type="file"]::file-selector-button,
  #view-import .catalog-actions input[type="file"]::file-selector-button {
    height: 28px; margin-right: 8px; padding: 0 12px; border: 0;
    border-radius: var(--r-xs); background: var(--brand); color: var(--surface);
    font: inherit; font-size: var(--fs-small); font-weight: 650; cursor: pointer;
    transition: background var(--t);
  }

  .orders-import-file input[type="file"]:hover::file-selector-button,
  .recipe-yield-file input[type="file"]:hover::file-selector-button,
  #view-import .catalog-actions input[type="file"]:hover::file-selector-button { background: var(--accent); }

  /* líneas: ~225 de presupuesto 250 */
}
/* ══ feedback ══════════════════════════════════════════════
   Cubre: .toast (+.show/.error), campanita (.notif-bell, .notif-bell-btn
     estados, .notif-badge, .notif-panel, .notif-panel-head, .notif-mark-all,
     .notif-list, .notif-empty, .notif-item[--red|--warn|--green|--info],
     .notif-dot, .notif-item-main, .notif-read-btn),
     vacíos unificados (.empty:not(td) [+.tight], .table-empty,
     .dashboard-empty[-plus], .reconciliation-empty-state,
     .coffee-dashboard-empty, .hr-empty, .mente-empty, .mente-emptyhint,
     .annex-empty, .mp-capture-empty),
     progreso (.import-progress, .view-loading, .hr-loading),
     avisos inline (.inline-status, .ar-bank-evidence,
     .inventory-import-status-ok/-bad, .inv-error, .mp-capture-error,
     .kitchen-email-notice, .hr-alert, .hr-success-note, .hr-blockers)
     y consola técnica .debug-box
   Fuente v1: styles.css ~L5098-5144 (vacíos), ~L5931-5962 (toast),
     ~L14420-14472 + ~L14686-14720 + ~L18025-18095 (campanita),
     ~L3474-3536 (import-progress), ~L5032-5050 (view-loading),
     ~L2994-3012, ~L7956, ~L12316, ~L12735, ~L14336-14359, ~L14492-14497,
     ~L16581-16627 + ~L17086-17089 (mp, fusión compacta last-wins),
     hr.css L83-96, L244-251; mente.css L79-84, L153 (fusionadas)
   Estados: completo (reposo/hover/focus/active vía base en botones; loading =
     progreso; error = .error/.bad/.inv-error; vacío = esta familia).
     disabled solo aplica al botón dentro de .mp-capture-empty (lo cubre base).
   NO cubre: .connection-progress (vive en shell — no duplicar).
   .overview-alert de v1 quedó SIN emisor en DOM/JS (verificado con grep:
     solo existía en styles.css) → no se porta.
   ═══════════════════════════════════════════════════════════ */

@layer componentes {

  .inventory-import-summary {
    margin-top: 12px;
    padding: 9px 11px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    color: var(--ink-2);
    font-size: var(--fs-small);
    font-weight: 650;
  }
  .inventory-import-summary.ok { color: var(--green); background: var(--green-soft); }
  .inventory-import-summary.bad { color: var(--red); background: var(--red-soft); }

  .inventory-import-status-ok,
  .inventory-import-status-bad { font-weight: 700; }
  .inventory-import-status-ok { color: var(--green); }
  .inventory-import-status-bad { color: var(--red); }

  /* Evidencia de conciliación mostrada debajo del control de CxC. */
  .ar-bank-evidence {
    display: block;
    max-width: 150px;
    margin-top: 4px;
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 700;
    line-height: 1.3;
  }
  .ar-bank-evidence.is-green { color: var(--green); }
  .ar-bank-evidence.is-amber { color: var(--amber); }
  .ar-bank-evidence.is-muted { color: var(--muted); }

  /* ── Toast ──────────────────────────────────────────────────────────
     Tarjeta clara con borde izquierdo de acento. No es interactivo:
     hover/focus/active no aplican. Texto largo: rompe con overflow-wrap. */
  .toast {
    position: fixed;
    right: 20px;
    bottom: 20px;
    z-index: var(--z-toast);
    max-width: 400px;
    padding: 12px 14px;
    border: 1px solid var(--line-2);
    border-left: 3px solid var(--accent);
    border-radius: var(--r);
    background: var(--surface);
    color: var(--ink);
    font-size: var(--fs-body);
    font-weight: 650;
    box-shadow: var(--shadow-md);
    overflow-wrap: anywhere;
    opacity: 0;
    transform: translateY(12px);
    pointer-events: none;
    transition: opacity var(--t), transform var(--t);
  }

  .toast.show { opacity: 1; transform: translateY(0); }

  .toast.error { border-left-color: var(--red); color: var(--red); }

  /* ── Campanita: botón y badge ───────────────────────────────────────
     El chrome del botón lo pone .icon-button (componente botones); aquí
     solo los estados de aviso y el contador. */
  .notif-bell { position: relative; }

  /* Pivote arriba: balanceo de campana real */
  .notif-bell-btn [data-icon] { display: inline-flex; transform-origin: 50% 3px; }

  .notif-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    display: grid;
    place-items: center;
    border: 2px solid var(--surface);
    border-radius: 999px;
    background: var(--red);
    color: var(--bg);
    font-size: var(--fs-caption);
    font-weight: 800;
    line-height: 1;
  }

  /* Con no leídos: campana tibia (ámbar) + sacudida suave periódica */
  .notif-bell.has-unread .notif-bell-btn {
    border-color: color-mix(in srgb, var(--amber) 45%, var(--line-2));
    color: var(--amber);
    background: color-mix(in srgb, var(--amber) 10%, transparent);
  }
  .notif-bell.has-unread .notif-bell-btn [data-icon] { animation: fb-bell-idle 6s ease-in-out 2.4s infinite; }
  .notif-bell.has-unread .notif-badge { animation: fb-badge-ping 2.4s ease-out .6s infinite; }

  /* Al llegar algo nuevo (JS pone .is-ringing en el botón mismo): repique +
     salto del contador. [data-icon] y .notif-badge son hijos del botón. */
  .notif-bell-btn.is-ringing [data-icon] { animation: fb-bell-swing 1.5s cubic-bezier(.35, .1, .25, 1) both; }
  .notif-bell-btn.is-ringing .notif-badge { animation: fb-badge-pop .55s var(--ease-pop) both; }

  /* ── Campanita: panel desplegable ─────────────────────────────────── */
  .notif-panel {
    position: fixed;               /* JS lo saca a <body> y lo posiciona */
    z-index: var(--z-popover);     /* v1: 150 crudo → canon popover */
    width: min(380px, calc(100vw - 32px));
    max-height: min(70vh, 520px);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border: 1px solid var(--line-2);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-md);
  }

  .notif-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--line);
  }

  .notif-panel-head strong { font-size: var(--fs-body-lg); }

  .notif-mark-all {
    border: 0;
    background: none;
    padding: 4px 6px;
    border-radius: var(--r-xs);
    font-size: var(--fs-small);
    font-weight: 650;
    color: var(--accent);
    cursor: pointer;
    transition: background var(--t);
  }
  .notif-mark-all:hover { background: var(--blue-soft); }
  .notif-mark-all:active { background: var(--surface-3); }

  .notif-list { overflow: auto; padding: 6px; }

  /* ── Campanita: ítems ─────────────────────────────────────────────── */
  .notif-item {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: start;
    gap: 10px;
    padding: 10px;
    border-radius: var(--r-sm);
    cursor: default;
  }
  .notif-item[data-notif-jump] { cursor: pointer; }
  .notif-item:hover { background: var(--surface-2); }

  /* Punto semántico por severidad (JS compone .notif-item--${sev};
     info es el color por defecto) */
  .notif-dot {
    width: 8px;
    height: 8px;
    margin-top: 6px;
    border-radius: 50%;
    background: var(--blue);
  }
  .notif-item--red .notif-dot { background: var(--red); }
  .notif-item--warn .notif-dot { background: var(--amber); }
  .notif-item--green .notif-dot { background: var(--green); }
  .notif-item.is-read .notif-dot { background: var(--line-2); }

  .notif-item-main { min-width: 0; }   /* títulos largos: rompen, no desbordan */
  .notif-item-main strong {
    display: block;
    font-size: var(--fs-body);
    font-weight: 650;
    color: var(--ink);
  }
  .notif-item.is-read .notif-item-main strong { font-weight: 550; color: var(--ink-2); }
  .notif-item-main small { display: block; margin-top: 2px; font-size: var(--fs-caption); color: var(--muted); line-height: 1.4; }

  .notif-read-btn {
    width: 22px;
    height: 22px;
    padding: 0;
    display: grid;
    place-items: center;
    border: 1px solid var(--line);
    border-radius: var(--r-xs);
    background: var(--surface);
    color: var(--green);
    cursor: pointer;   /* focus-visible y disabled los resuelve base */
  }
  .notif-read-btn svg { width: 13px; height: 13px; }
  .notif-item.is-read .notif-read-btn { color: var(--green); border-color: var(--green-soft); background: var(--green-soft); }
  .notif-item.is-unread .notif-read-btn:hover { border-color: var(--green); }

  /* ── Vacíos: UNA receta ─────────────────────────────────────────────
     Borde discontinuo + ícono + texto tenue sobre trama de puntos.
     td.empty vive en base. .dashboard-empty y .hr-empty traen su propio
     ícono en el markup, por eso no llevan ::before. */
  .empty:not(td),
  .table-empty,
  .notif-empty,
  .annex-empty,
  .reconciliation-empty-state,
  .coffee-dashboard-empty,
  .dashboard-empty,
  .hr-empty,
  .mente-empty,
  .mp-capture-empty {
    position: relative;
    min-height: 128px;
    display: grid;
    place-items: center;
    gap: 8px;
    padding: 24px;
    overflow: hidden;
    border: 1px dashed var(--line-2);
    border-radius: var(--r);
    background:
      linear-gradient(0deg,
        color-mix(in srgb, var(--bg) 76%, transparent),
        color-mix(in srgb, var(--bg) 76%, transparent)),
      repeating-radial-gradient(circle at 78% 12%,
        color-mix(in srgb, var(--accent) 11%, transparent) 0 1px,
        transparent 1px 24px);
    color: var(--muted);
    text-align: center;
    font-size: var(--fs-body);
    font-weight: 550;
  }

  .empty:not(td)::before,
  .table-empty::before,
  .notif-empty::before,
  .reconciliation-empty-state::before,
  .coffee-dashboard-empty::before {
    content: "";
    width: 34px;
    height: 34px;
    border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
    border-radius: 50%;
    box-shadow: inset 0 0 0 8px color-mix(in srgb, var(--green-vivo) 6%, transparent);
  }

  /* Hijos con voz común en toda la familia */
  :is(.empty, .table-empty, .dashboard-empty, .coffee-dashboard-empty,
      .hr-empty, .mp-capture-empty) strong { color: var(--ink-2); font-weight: 650; }
  :is(.empty, .table-empty, .coffee-dashboard-empty) small { display: block; font-size: var(--fs-small); font-weight: 550; color: var(--muted-2); }
  :is(.empty, .dashboard-empty, .reconciliation-empty-state, .hr-empty) p { margin: 0; max-width: 440px; line-height: 1.55; }

  /* Variante compacta: mensajes de una línea, sin ícono ni altura mínima */
  .empty.tight,
  .mente-empty { min-height: 0; padding: 12px; }
  .empty.tight::before { content: none; }

  .annex-empty { margin: 0; }   /* llega como <p class="empty annex-empty"> */

  .dashboard-empty {
    min-height: 60vh;
    gap: 14px;
    padding: 48px 24px;
    border-radius: var(--r-lg);
  }
  .dashboard-empty h2 { margin: 0; color: var(--ink-2); }
  .dashboard-empty p { max-width: 470px; }
  .dashboard-empty-plus {
    width: 66px;
    height: 66px;
    display: grid;
    place-items: center;
    border: 2px dashed var(--line-2);
    border-radius: 50%;
    color: var(--line-2);
  }
  .dashboard-empty-plus svg { width: 30px; height: 30px; }

  /* v1 traía fondo crema pre-canon (#f8f5ef): muere aquí, receta única */
  .reconciliation-empty-state { min-height: 280px; font-size: var(--fs-body-lg); }

  .hr-empty { min-height: 230px; padding: 40px; }
  .hr-empty > span {
    width: 48px;
    height: 48px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--blue-soft);
    color: var(--accent);
    font-size: var(--fs-h2);
  }
  .hr-empty h4 {
    margin: 6px 0 0;
    font-family: var(--font-numeric);
    font-size: var(--fs-h3);
    font-weight: 700;
    color: var(--ink-2);
  }

  /* Vacío con CTA (fusión compacta v1): texto a la izquierda, botón a la
     derecha; el disabled del botón lo cubre base */
  .mp-capture-empty {
    min-height: 0;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    justify-items: start;
    padding: 14px 16px;
    text-align: left;
  }
  .mp-capture-empty span { font-size: var(--fs-caption); line-height: 1.45; color: var(--muted); }
  .mp-capture-empty button { grid-column: 2; grid-row: 1 / span 2; }

  /* Sobreimpreso centrado en el lienzo del Taller: no lleva caja */
  .mente-emptyhint {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    z-index: var(--z-dropdown);
    text-align: center;
    font-size: var(--fs-body-lg);
    color: var(--muted);
    pointer-events: none;
  }
  .mente-emptyhint strong { color: var(--ink-2); font-weight: 650; }

  /* ── Progreso ───────────────────────────────────────────────────────
     .connection-progress es de shell. Aquí la barra de importaciones:
     determinada (ancho por --import-progress vía JS), .indeterminate,
     .ok y .bad. La vista de importación fija --import-accent por columna. */
  .import-progress {
    --import-accent: var(--accent);
    --import-progress: 0%;
    position: relative;
    height: 7px;
    overflow: hidden;
    border-radius: 999px;
    background: color-mix(in srgb, var(--import-accent) 11%, transparent);
    box-shadow: inset 0 1px 1px color-mix(in srgb, var(--ink) 8%, transparent);
  }
  .import-progress span {
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--import-progress);
    border-radius: inherit;
    background: linear-gradient(90deg,
      var(--import-accent),
      color-mix(in srgb, var(--import-accent) 40%, var(--accent)));
    transition: width .24s var(--ease);
  }
  .import-progress.indeterminate span { width: 44%; animation: fb-progress-slide 1.05s ease-in-out infinite; }
  .import-progress.indeterminate span::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg,
      transparent,
      color-mix(in srgb, var(--bg) 58%, transparent),
      transparent);
    transform: translateX(-100%);
    animation: fb-shimmer 1s linear infinite;
  }
  .import-progress.ok span { background: linear-gradient(90deg, var(--green), var(--green-vivo)); }
  .import-progress.bad { background: var(--red-soft); }
  .import-progress.bad span { background: linear-gradient(90deg, var(--red-deep), color-mix(in srgb, var(--red) 55%, var(--bg))); }

  /* Barra de carga de vista: hilo de 3px pegado arriba mientras JS marca
     .view-loading sobre la vista activa */
  .view.active.view-loading::before {
    content: "";
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    display: block;
    height: 3px;
    margin: -3px 0 0;
    border-radius: 999px;
    background: linear-gradient(90deg,
      transparent, var(--green-vivo), var(--accent), transparent);
    background-size: 220% 100%;
    animation: fb-view-sweep 1.05s ease-in-out infinite;
  }

  .hr-loading {
    min-height: 420px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
  }
  .hr-loading span {
    width: 28px;
    height: 28px;
    border: 3px solid var(--line-2);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: fb-spin .8s linear infinite;
  }
  .hr-loading p { margin: 0; color: var(--muted); font-size: var(--fs-small); }

  /* ── Avisos inline ──────────────────────────────────────────────────
     Lenguaje único: banda de tinte + borde izquierdo 3px semántico. */
  .inline-status {
    display: inline-block;
    padding: 4px 8px;
    border-left: 3px solid var(--line-strong);
    border-radius: var(--r-xs);
    background: var(--surface-2);
    font-size: var(--fs-small);
    font-weight: 650;
    color: var(--muted);
    overflow-wrap: anywhere;
  }
  .inline-status.ok {
    border-left-color: var(--green);
    background: color-mix(in srgb, var(--green-vivo) 6%, var(--bg));
    color: var(--green);
  }
  .inline-status.bad {
    border-left-color: var(--red);
    background: color-mix(in srgb, var(--red) 6%, var(--bg));
    color: var(--red);
  }

  /* Monta sobre .empty / td.empty: recolorea el vacío a error */
  .inv-error { color: var(--red); border-color: color-mix(in srgb, var(--red) 24%, transparent); }

  .mp-capture-error {
    margin: 12px 0 0;
    padding: 8px 10px;
    border-left: 3px solid var(--red);
    border-radius: var(--r-xs);
    background: var(--red-soft);
    color: var(--red);
    font-size: var(--fs-caption);
  }

  .kitchen-email-notice {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin: 8px 0 12px;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-left: 3px solid var(--line-strong);
    border-radius: var(--r-sm);
    background: var(--surface-2);
  }
  .kitchen-email-notice.is-sent { border-color: color-mix(in srgb, var(--green) 24%, transparent); border-left-color: var(--green); background: color-mix(in srgb, var(--green-vivo) 6%, var(--bg)); }
  .kitchen-email-notice.is-pending { border-color: color-mix(in srgb, var(--red) 24%, transparent); border-left-color: var(--red); background: color-mix(in srgb, var(--red) 6%, var(--bg)); }
  .kitchen-email-notice > div { display: grid; gap: 2px; min-width: 0; }
  .kitchen-email-notice strong { font-size: var(--fs-small); }
  .kitchen-email-notice small {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--muted);
    font-size: var(--fs-caption);
  }

  .hr-alert {
    margin-top: 14px;
    padding: 10px 12px;
    border-left: 3px solid var(--amber);
    border-radius: var(--r-sm);
    background: var(--amber-soft);
    color: var(--amber);
    font-size: var(--fs-small);
  }

  /* Franjas de cierre RRHH: van de canto a canto del panel, sin radio */
  .hr-success-note {
    padding: 12px 24px;
    border-left: 3px solid var(--green);
    background: var(--green-soft);
    color: var(--green);
    font-size: var(--fs-caption);
  }

  .hr-blockers {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    padding: 12px 24px;
    border-left: 3px solid var(--amber);
    background: var(--amber-soft);
  }
  .hr-blockers > strong {
    color: var(--amber);
    font-size: var(--fs-caption);
    white-space: nowrap;
  }
  .hr-blockers > div { display: flex; flex-wrap: wrap; gap: 6px; }
  .hr-blockers span {
    padding: 4px 8px;
    border: 1px solid color-mix(in srgb, var(--amber) 30%, transparent);
    border-radius: 999px;
    background: var(--bg);
    color: var(--amber);
    font-size: var(--fs-caption);
  }

  /* Consola de proveedores externos: superficie deliberadamente oscura
     para separar salida técnica del resto de la interfaz. */
  .debug-box {
    max-height: 320px;
    overflow: auto;
    padding: 12px;
    border: 1px solid color-mix(in srgb, var(--surface) 18%, transparent);
    border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--ink) 88%, black);
    color: color-mix(in srgb, var(--surface) 88%, var(--cyan));
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--fs-caption);
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
  }

  .owner-account-footnote {
    margin: 0 0 16px;
    padding: 12px 14px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    color: var(--muted);
    font-size: var(--fs-caption);
    line-height: 1.5;
  }
  .owner-account-footnote strong { color: var(--ink-2); }
  .owner-account-no-action,
  .accounting-lines-truncated { color: var(--muted); font-size: var(--fs-caption); }
  .accounting-lines-truncated { padding: 10px; background: var(--surface-2); text-align: center; }

  @media (max-width: 680px) {
    .hr-blockers { flex-direction: column; align-items: stretch; }
  }

  /* ── Keyframes (prefijo fb- para no chocar con otros archivos) ────── */
  @keyframes fb-bell-swing {
    0% { transform: rotate(0); }
    12% { transform: rotate(18deg); }
    24% { transform: rotate(-15deg); }
    36% { transform: rotate(11deg); }
    48% { transform: rotate(-8deg); }
    60% { transform: rotate(5deg); }
    72% { transform: rotate(-3deg); }
    84% { transform: rotate(1deg); }
    100% { transform: rotate(0); }
  }
  @keyframes fb-bell-idle {
    0%, 82%, 100% { transform: rotate(0); }
    85% { transform: rotate(11deg); }
    89% { transform: rotate(-9deg); }
    93% { transform: rotate(6deg); }
    97% { transform: rotate(-3deg); }
  }
  @keyframes fb-badge-ping {
    0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--red) 50%, transparent); }
    75% { box-shadow: 0 0 0 8px transparent; }
    100% { box-shadow: 0 0 0 0 transparent; }
  }
  @keyframes fb-badge-pop {
    0% { transform: scale(1); }
    40% { transform: scale(1.45); }
    100% { transform: scale(1); }
  }
  @keyframes fb-progress-slide {
    0% { left: -48%; width: 42%; }
    50% { left: 34%; width: 52%; }
    100% { left: 106%; width: 42%; }
  }
  @keyframes fb-shimmer { to { transform: translateX(100%); } }
  @keyframes fb-view-sweep {
    from { background-position: 100% 0; }
    to { background-position: -120% 0; }
  }
  @keyframes fb-spin { to { transform: rotate(360deg); } }

  /* Movimiento decorativo fuera; spinner y barras se quedan (informan) */
  @media (prefers-reduced-motion: reduce) {
    .notif-bell.has-unread .notif-bell-btn [data-icon],
    .notif-bell.has-unread .notif-badge,
    .notif-bell .notif-bell-btn.is-ringing [data-icon],
    .notif-bell .notif-bell-btn.is-ringing .notif-badge { animation: none; }
  }
}

/* líneas: ~553 de presupuesto 350 — excede: agrupa 4 subsistemas (toast,
   campanita completa, familia de vacíos en 12 puertos, progreso/loading,
   avisos inline en 7 puertos). Recortado ~10% (612→553) sin tocar estados
   ni cobertura; bajar más exige recortar puertos o dividir el archivo
   (candidato natural: campanita → su propio componente, ~140 líneas). */
/* ══ panel de filtros ═══════════════════════════════════════════════════════
   Cubre: .filtros-btn (+ .tiene-activos, .filtros-cuenta), .filtros-drawer
          (+ .abierto), .filtros-velo, .filtros-cab, .filtros-cuerpo,
          .filtros-pie, y el re-encuadre de las toolbars de vista cuando
          viven DENTRO del panel.
   Fuente v1: no existe — componente nuevo. La barra de filtros de cada vista
          se muda aquí tal cual (mismos nodos, mismos listeners), igual que
          el período se muda al header.
   Estados: cerrado · abierto · con filtros activos (contador en el botón).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── Disparador en la barra ──────────────────────────────────────────── */
  .filtros-btn {
    display: inline-flex; align-items: center; gap: 7px;
    min-height: var(--control-h-compact);
    padding: 0 12px;
    border: 1px solid var(--line-2);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--ink-2);
    font-size: var(--fs-small); font-weight: 650;
    white-space: nowrap;
    transition: border-color var(--t), background var(--t), color var(--t);
  }
  .filtros-btn:hover:not(:disabled) { border-color: var(--line-strong); background: var(--surface-2); }

  /* Vista sin filtros: el botón NO se va, se apaga. Así su sitio en la barra
     es el mismo siempre y se lee de un vistazo que aquí no hay nada que
     filtrar (en vez de tener que notar que falta un botón). */
  .filtros-btn:disabled {
    color: var(--muted-2);
    background: var(--surface-2);
    border-color: var(--line);
    cursor: default;
    opacity: 1;   /* el gris ya comunica: no hace falta además transparentar */
  }

  /* Con filtros puestos el botón deja de ser neutro: avisa que la vista
     está recortada, que es la duda típica al leer una cifra rara. */
  .filtros-btn.tiene-activos {
    border-color: color-mix(in srgb, var(--accent) 40%, transparent);
    background: color-mix(in srgb, var(--accent) 8%, var(--surface));
    color: var(--accent);
  }
  .filtros-cuenta {
    min-width: 17px; height: 17px; padding: 0 5px;
    display: none; place-items: center;
    border-radius: 999px;
    background: var(--accent);
    color: var(--surface);
    font-size: var(--fs-caption); font-weight: 700; line-height: 1;
    font-variant-numeric: tabular-nums;
  }
  .filtros-btn.tiene-activos .filtros-cuenta { display: grid; }

  /* ── Velo ────────────────────────────────────────────────────────────── */
  /* Velo apenas insinuado: el panel ya no es un cajón a pantalla completa
     sino un globo anclado al botón. Con el velo de modal, abrir los filtros
     apagaba toda la vista para enseñar una nube de 360px. Sigue estando para
     cazar el clic fuera y para que se note que hay algo abierto. */
  .filtros-velo {
    position: fixed; inset: 0;
    z-index: var(--z-modal);
    background: color-mix(in srgb, var(--ink) 12%, transparent);
    animation: filtros-velo-in .16s var(--ease);
  }
  .filtros-velo[hidden] { display: none; }
  @keyframes filtros-velo-in { from { opacity: 0; } }

  /* ══ El panel ════════════════════════════════════════════════════════════
     No baja del techo: sale DEL BOTÓN, que es de donde uno espera que salga lo
     que acaba de pulsar. El JS le pasa las coordenadas (--nube-x/-y) tomadas
     del propio botón.

     Fue una nube de cómic —silueta lobulada por máscara y un rabo de tres
     burbujas— y se retiró: la gracia del recorte no compensaba lo que costaba
     leer el contenido pegado a un filo curvo. Panel liso, y la relación con el
     botón la cuenta el anclaje, no un adorno. */
  .filtros-nube {
    position: fixed;
    left: var(--nube-x, auto);
    top: var(--nube-y, 0);
    z-index: var(--z-modal-top);
    width: min(360px, calc(100vw - 28px));
    max-height: var(--nube-alto, 70vh);
    display: flex;
    transform-origin: var(--nube-origen, 100% 0);
    transform: scale(.94);
    opacity: 0;
    transition: transform .2s var(--ease-pop), opacity .16s var(--ease);
  }
  .filtros-nube[hidden] { display: none; }
  .filtros-nube.abierto { transform: none; opacity: 1; }

  .filtros-drawer {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    max-height: inherit;
    display: flex; flex-direction: column;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-lg);
  }
  .filtros-drawer[hidden] { display: none; }

  @media (prefers-reduced-motion: reduce) {
    .filtros-nube { transition: none; transform: none; }
  }

  /* Con la silueta lobulada, la cabecera y el pie iban metidos hacia dentro
     para que la curva no les mordiera el filete. Panel recto: los filetes
     cruzan de lado a lado. */
  .filtros-cab {
    flex: none;
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 20px 22px 14px;
    border-bottom: 1px solid var(--line);
  }
  .filtros-cab h2 {
    margin: 0;
    font-family: var(--font-display); font-weight: 400;
    font-size: var(--fs-h3); letter-spacing: -.02em;
    color: var(--brand);
  }

  /* Un filtro por fila, con aire entre ellos: la columna se lee como lista. */
  .filtros-cuerpo {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 18px;
    padding: 16px 22px;
  }

  .filtros-pie {
    flex: none;
    display: flex; gap: 8px;
    padding: 14px 22px 18px;
    border-top: 1px solid var(--line);
  }
  .filtros-pie > * { flex: 1 1 auto; justify-content: center; }

  .filtros-vacio {
    color: var(--muted-2);
    font-size: var(--fs-body);
    text-align: center;
    padding: 28px 12px;
  }

  @media (max-width: 680px) {
    .filtros-nube { width: min(320px, calc(100vw - 20px)); }
  }
}

/* ── Qué se esconde de la vista y qué se manufactura en el panel ────────────
   Va en @layer utilidades y no en componentes a propósito: varias vistas
   definen la geometría de su propia barra en @layer vistas (Ingeniería de
   menú la declara como grid de 4 columnas que suman 800px), y esa capa gana a
   componentes pase lo que pase con la especificidad. Dentro del panel manda
   el panel. */
@layer utilidades {

  /* ── La vista viene limpia de FILTROS, no de acciones ni del buscador ────
     Antes se escondía la barra entera y con ella se iban "+ Nuevo pedido",
     "Importar Excel" o "Sincronizar ítems": acciones principales que
     desaparecían de la vista y solo reaparecían detrás de un botón que dice
     "Filtros". Ahora se esconde exactamente lo que filtra —misma definición
     que usa el JS— y la barra se queda a la vista con sus acciones y con el
     buscador, que se usa a cada rato y no merece un clic previo. */
  .view :is(.book-tools, .sales-tools, .report-tools, .menu-tools,
      .overview-tools, .report-filter, .report-filter-strip,
      .consumption-filter-strip, .consumption-advanced-filters,
      .menu-filter-strip, .benchmark-tools, .receivables-tools,
      .daily-date-tools, .coffee-calendar-toolbar, .recipe-yield-tools,
      .annex-controls, .catalog-photo-tools)
    > :is(.segmented-control, .date-range-filter, details, select, .menu-exclusion-control,
          input:not([type="file"]):not([type="hidden"]):not([type="button"]):not([type="submit"]):not([type="search"]),
          label:has(select),
          label:has(input:not([type="file"]):not([type="hidden"]):not([type="search"]))) {
    display: none;
  }

  /* Barra que solo tenía filtros: sin nada que mostrar no deja su hueco.
     Lo marca el JS, que puede mirar hijo por hijo. */
  .view :is(.book-tools, .sales-tools, .report-tools, .menu-tools,
      .overview-tools, .report-filter, .report-filter-strip,
      .consumption-filter-strip, .consumption-advanced-filters,
      .menu-filter-strip, .benchmark-tools, .receivables-tools,
      .daily-date-tools, .coffee-calendar-toolbar, .recipe-yield-tools,
      .annex-controls, .catalog-photo-tools)[data-solo-filtros="1"] { display: none; }

  /* ── La barra que sobrevive en la vista: buscador + acciones ─────────────
     Se re-encuadra como fila simple. Varias de estas barras son rejillas con
     columnas declaradas para los filtros que ahora viven en el cajón: esas
     columnas quedaban vacías pero seguían ocupando su ancho, y en Comparador
     de precios el buscador terminaba aplastado a 26px contra cinco columnas
     fantasma. */
  .view :is(.book-tools, .sales-tools, .report-tools, .menu-tools,
      .overview-tools, .report-filter, .report-filter-strip,
      .consumption-filter-strip, .consumption-advanced-filters,
      .menu-filter-strip, .benchmark-tools, .receivables-tools,
      .daily-date-tools, .coffee-calendar-toolbar, .recipe-yield-tools,
      .annex-controls, .catalog-photo-tools)[data-solo-filtros="0"] {
    display: flex;
    /* nowrap: la barra ya solo lleva buscador y un par de acciones — caben en
       una fila. Con wrap el CSV se descolgaba a una segunda línea y la
       cabecera crecía 50px por una fila con un botón. */
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
    grid-template-columns: none;
    /* Sin encoger: como ítem de la cabecera se comprimía a 360px contra un
       título con nota larga, y el buscador y el CSV se apilaban en dos filas.
       Si no cabe al lado del título, que baje entera a la línea siguiente. */
    flex: 0 0 auto;
  }

  /* El período se mudó a la barra superior y dejó su rótulo huérfano flotando
     en la barra de la vista ("Período" solo, sin control debajo). */
  .view .benchmark-field:not(:has(:is(select, .date-range-filter, .search-box,
      input:not([type="hidden"])))) {
    display: none;
  }

  /* El buscador tiene ancho propio: ni se estira a la barra entera (1.246px
     en Consumo al quedarse solo) ni se deja aplastar por un rótulo largo. */
  .view :is(.book-tools, .sales-tools, .report-tools, .menu-tools,
      .overview-tools, .report-filter, .report-filter-strip,
      .consumption-filter-strip, .consumption-advanced-filters,
      .menu-filter-strip, .benchmark-tools, .receivables-tools,
      .daily-date-tools, .coffee-calendar-toolbar, .recipe-yield-tools,
      .annex-controls, .catalog-photo-tools) :is(.search-box, input[type="search"]) {
    flex: 0 1 320px;
    min-width: 220px;
  }

  /* El rótulo de la acción no se parte: "Reconciliar NC" en dos líneas subía
     la cabecera de la tarjeta 18px por una palabra. */
  .view :is(.book-tools, .sales-tools, .report-tools, .menu-tools,
      .overview-tools, .report-filter, .report-filter-strip,
      .consumption-filter-strip, .menu-filter-strip, .benchmark-tools,
      .receivables-tools, .daily-date-tools, .recipe-yield-tools) > button {
    flex: none;
    white-space: nowrap;
  }
  .view :is(.book-tools, .sales-tools, .report-tools, .menu-tools,
      .overview-tools, .report-filter, .report-filter-strip,
      .consumption-filter-strip, .consumption-advanced-filters,
      .menu-filter-strip, .benchmark-tools, .receivables-tools,
      .daily-date-tools, .coffee-calendar-toolbar, .recipe-yield-tools,
      .annex-controls, .catalog-photo-tools) > label:has(input[type="search"]) { width: auto; }

  /* ── Normalización del ítem: aquí y no en @layer componentes ─────────────
     Tres trampas, todas del mismo origen: el control trae propiedades flex
     pensadas para una barra HORIZONTAL y en columna significan otra cosa.
       · min-width:auto (inicial) → un segmentado de 4 chips sin partir pedía
         300px más que el panel y lo desbordaba.
       · flex-basis:100% → en fila es "ocupa todo el ancho"; en columna es
         "ocupa todo el ALTO", y el segmentado de Consumo se estiraba a 772px
         con los chips flotando en medio de una caja vacía.
       · flex-shrink:1 → les robaba alto hasta disparar el wrap.
     Va en utilidades porque `flex-basis:100%` lo declara la vista dueña
     (@layer vistas) y esa capa gana a componentes pase lo que pase con la
     especificidad. */
  .filtros-cuerpo > * {
    min-width: 0;
    max-width: 100%;
    flex: 0 0 auto;
  }

  /* ══ Manufactura del panel ══════════════════════════════════════════════
     Cada filtro llega como el mismo nodo que vivía en la vista, con su
     listener intacto, pero se re-encuadra por completo: en la barra
     horizontal el rótulo iba al costado y el control tomaba el resto; aquí
     el rótulo va encima y el control ocupa la fila entera. */

  /* ── Campo con rótulo ─────────────────────────────────────────────────────
     Descendiente y no hijo directo: "Más filtros" (Consumo) trae cuatro
     campos anidados dentro del <details>, y con `>` se quedaban sin encuadre
     y sin cheurón. Se excluyen las casillas, que son texto de corrido. */
  .filtros-cuerpo label:is(:has(select), :has(input:not([type="checkbox"]):not([type="radio"]))) {
    position: relative;
    display: grid;
    grid-template-columns: 1fr;
    gap: 6px;
    width: 100%;
    margin: 0;
    white-space: normal;
  }

  /* ── Controles: alto de barra densa, fondo banda ──────────────────────────
     Seis cajas blancas de 40px apiladas llenan el panel de marcos y ninguna
     se distingue de la otra. A 34px sobre banda el control se lee como campo
     y el ojo baja por la columna de rótulos, no por la de bordes. */
  .filtros-cuerpo :is(select, input, .date-range-trigger) {
    width: 100%;
    min-width: 0;
    min-height: 34px;
    height: 34px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    color: var(--ink);
    font-size: var(--fs-body);
  }
  .filtros-cuerpo :is(select, input):hover { border-color: var(--line-2); }
  /* Al enfocar se blanquea: marca cuál de los seis estás tocando. */
  .filtros-cuerpo :is(select, input):focus {
    background: var(--surface);
    border-color: var(--accent);
  }

  /* Cheurón propio: el del sistema operativo cambia de forma en cada
     navegador y no hay dos campos que se vean igual. Se dibuja aquí, sobre el
     <label>, porque un <select> es un elemento reemplazado y no admite
     pseudo-elementos. */
  .filtros-cuerpo label:has(select) select {
    appearance: none;
    -webkit-appearance: none;
    padding: 0 32px 0 11px;
  }
  .filtros-cuerpo label:has(select)::after {
    content: "";
    position: absolute;
    right: 13px;
    bottom: 13px;
    width: 7px;
    height: 7px;
    border-right: 1.5px solid var(--muted);
    border-bottom: 1.5px solid var(--muted);
    transform: rotate(45deg);
    pointer-events: none;
  }

  /* ── Segmentado: chips que envuelven ───────────────────────────────────── */
  .filtros-cuerpo .segmented-control {
    /* Envuelve por CONTENIDO, no por rejilla de ancho fijo: con columnas de
       70px "Todo gasto" salía cortado a media palabra. Aquí cada chip pide lo
       que su rótulo necesita, crece para repartirse la fila y baja a la
       siguiente cuando no cabe. */
    display: flex;
    flex-wrap: wrap;
    gap: 3px;
    width: 100%;
    height: auto;        /* en la barra el alto va clavado y la 2ª fila se salía */
    min-height: 34px;
    padding: 3px;
    border-radius: var(--r-sm);
  }
  .filtros-cuerpo .segmented-control button {
    flex: 1 1 auto;      /* sin min-width:0: nunca por debajo de su rótulo */
    max-width: 100%;
    height: 28px;
    padding: 0 10px;
    border-radius: var(--r-xs);
    white-space: nowrap;
  }

  /* ── Filtros avanzados (<details>) ─────────────────────────────────────── */
  .filtros-cuerpo > details { width: 100%; }
  .filtros-cuerpo > details > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: 34px;
    padding: 0 11px;
    border: 1px dashed var(--line-2);
    border-radius: var(--r-sm);
    background: none;
    color: var(--ink-2);
    font-size: var(--fs-small);
    font-weight: 650;
  }
  .filtros-cuerpo > details[open] > summary { margin-bottom: 14px; }
  .filtros-cuerpo > details > div { display: grid; gap: 14px; }

  /* ── Rango de fechas ──────────────────────────────────────────────────── */
  .filtros-cuerpo .date-range-filter { width: 100%; }

  /* ── Bloque compuesto (Excluir del cálculo): campo + chips + Limpiar ────── */
  .filtros-cuerpo .menu-exclusion-control {
    display: grid;
    gap: 10px;
    width: 100%;
  }
  .filtros-cuerpo .menu-exclusion-control .secondary-action {
    justify-self: start;
    min-height: var(--control-h-compact);
    padding: 0 12px;
  }

  /* Red de seguridad: ningún filtro empuja el panel a lo ancho. */
  .filtros-cuerpo { overflow-x: hidden; }
}

/* líneas: 467 de presupuesto 300 */
/* ══ fuego ═══════════════════════════════════════════════════════════════════
   Cubre: .fuego-lienzo, .fuego-chispas y lo que le cambian a .ph-toolbox y
          .topbar cuando el modo está encendido (:root[data-fuego="1"]).
   Fuente v1: no existe — modo nuevo, opcional desde Ajustes y apagado por
          defecto. Sin él, ni una regla de aquí se activa.
   Contrato: el fuego se pinta en <canvas> DETRÁS del vidrio de la cápsula. El
          backdrop-filter del toolbox lo difumina, y por eso las llamas se ven
          a través sin que las cifras pierdan contraste. Ese orden —fuego
          abajo, vidrio encima— es el que hace que esto sea legible.
   Estados: apagado (por defecto) · encendido · movimiento reducido (queda
          quieto en una brasa, no se anima).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  /* La paleta la aplica el JS al pintar; aquí solo viven las capas y su sitio.
     Los colores salen del rojo del punto del wordmark (--dot) y del ámbar del
     sistema: arde en la marca, no en un naranja de stock. */

  /* Sin z-index a propósito: dentro de la cápsula (que aísla su contexto) el
     orden del DOM basta. El lienzo va primero y sin posicionar los hijos lo
     taparía —un absoluto pinta sobre lo no posicionado—, así que a los
     compartimentos les basta `position: relative` para quedar encima. Menos
     números mágicos que mantener sincronizados. */
  /* La caja existe solo para dar sitio al lienzo justo detrás de la cápsula.
     La monta y la desarma el JS al encender y apagar. */
  .fuego-caja {
    position: relative;
    border-radius: 999px;
  }

  .fuego-lienzo {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border-radius: inherit;
    pointer-events: none;
  }

  /* Las chispas se salen del molde a propósito, así que no las recorta la
     cápsula: van por encima y con su propia caja, más alta que la barra. */
  .fuego-chispas {
    position: absolute;
    inset: -64px -16px -8px;
    width: calc(100% + 32px);
    height: calc(100% + 72px);
    pointer-events: none;
  }

  /* ── Lo que el fuego le cambia a la cápsula ─────────────────────────────
     El contenido tiene que quedar POR ENCIMA del lienzo de fuego; sin esto
     las llamas se pintan sobre el título.
     El :not(canvas) NO es cosmético: sin él la regla alcanzaba a los propios
     lienzos, los sacaba de `absolute` y pasaban a ser celdas de la rejilla —
     la cápsula crecía de 54px a 366. */
  :root[data-fuego="1"] .ph-toolbox > :not(canvas) { position: relative; }

  /* El vidrio se calienta un punto: sin esto la cápsula sigue siendo una
     lámina fría con un incendio detrás, y se lee como dos cosas distintas. */
  :root[data-fuego="1"] .ph-toolbox {
    border-color: color-mix(in srgb, var(--amber-bright) 55%, transparent);
    box-shadow:
      inset 0 1px 0 color-mix(in srgb, var(--surface) 90%, transparent),
      inset 0 -2px 8px -2px color-mix(in srgb, var(--dot) 40%, transparent),
      0 12px 34px -16px color-mix(in srgb, var(--dot) 45%, transparent);
  }

  /* ── Y a la franja del nav ───────────────────────────────────────────────
     Ahí no hay vidrio encima que difumine, así que el búfer escalado se veía
     como pixel art: lleva su propio desenfoque. Y los rótulos necesitan
     sombra propia para sobrevivir sobre las llamas. */
  :root[data-fuego="1"] .topbar { position: relative; overflow: hidden; }
  :root[data-fuego="1"] .topbar > .fuego-lienzo {
    inset: -6px;
    width: calc(100% + 12px);
    height: calc(100% + 12px);
    opacity: .62;
    filter: blur(5px) saturate(120%);
  }
  :root[data-fuego="1"] .topbar > :not(canvas) { position: relative; }
  :root[data-fuego="1"] .topbar :is(.tb-cat, .brandmark, .user-identity, .marca b) {
    text-shadow: 0 1px 3px color-mix(in srgb, var(--ink) 85%, transparent);
  }

  /* Sin movimiento: el JS pinta unos pocos cuadros y se detiene. La brasa
     queda, la animación no. */
  @media (prefers-reduced-motion: reduce) {
    .fuego-chispas { display: none; }
  }
}

/* líneas: 94 de presupuesto 120 */
/* ══ hr — sistema RRHH completo (isla) ═══════════════════════════════════════
   Cubre: .hr-module, .hr-hero(+-badge), .hr-kpis/.hr-third-party-kpis/
     .hr-vacation-kpis(+article.has-attention), .hr-stage, .hr-section-head,
     .hr-settings-card h3/.hr-legal-card h3 (título compartido), .hr-head-
     actions/.hr-period-actions, .hr-inline-select, .hr-table-wrap/.hr-table
     (+tr.is-muted), .hr-row-actions/.hr-card-actions (botones sin clase),
     .hr-net-cell, .hr-status(+approved/paid/calculated/closed/draft),
     .hr-period-card(+-title/-metrics), .hr-compliance-hero(+.is-complete)/
     -score, .hr-compliance-grid/-card(+dl/dd), .hr-filing-proof, .hr-third-
     party-sources/-kpis/-ledger/-table, .hr-source-workflow(+.is-caja)/
     -license-channels, .hr-inbox-state/-banner, .hr-evidence-strip, .hr-
     third-party-applied, .hr-import-summary/-preview, .hr-vacation-
     explainer/-table/-legal/-detail/-balance/-panel(+-title)/-day-preview/
     -request-list/-empty/-adjustments/-entitlements/-sources, .hr-settings-
     grid/-card/-legal-card/-card-head/-section(+-head), .hr-affiliation-
     note, .hr-auto-code, .hr-source-list, .hr-extraction-review/-list,
     .hr-automation-note, .hr-form(+-section/-grid/-grid-3), .hr-field
     (+-wide/.has-error), .hr-check-grid/-check, .hr-form-hint/-actions,
     .hr-smart-defaults, .hr-guided-details, .hr-history, .hr-worker-strip,
     .hr-form-subgrid, .hr-variable-head/-list/-row, .hr-historical-rules/
     -explainer/-summary/-result/-table-wrap/-table/-empty/-review-details/
     -minimum-details, .hr-history-months, .hr-export-result/-summary/
     -messages, .hr-delivery-history, .hr-sim-picker/-workspace/-form-panel/
     -fields/-field/-grid/-check/-disclaimer/-person-mode, .hr-contract-doc-
     toggle/-alt-times/-doc-summary, .hr-sim-inline-help/-result(+-head/
     -actions)/-placeholder/-tags/-tag/-kpis/-columns/-items/-none/-note/
     -warnings/-base-card/-total-card/-legal/-hypothetical/-chart/-bar.
   NO cubre (ya tienen dueño, verificado con grep 2026-08-09 — no se duplica):
     .hr-tabs → toolbar.css. .hr-empty/.hr-loading/.hr-alert/.hr-success-
     note/.hr-blockers → feedback.css. .hr-document-upload(+-upload-icon/
     -upload-filename) → dropzone.css. .hr-modal(+-wide/-head/-backdrop,
     .hr-modal > .hr-alert) → modal.css. Familias .hr-slip-* y
     .hr-certificate-* →
     08-print.css (documentos protegidos, no se tocan).
   Fuente v1: public/assets/hr.css completo (619 líneas), menos los bloques
     ya adoptados arriba.
   Estados: interactivo cubierto — reposo · hover · focus-visible (02-base:
     aro accent) · disabled (.hr-contract-doc-toggle button, checkbox de
     .hr-check, inputs de .hr-field/.hr-sim-field vía :disabled nativo) ·
     vacío (.hr-vacation-empty/.hr-sim-none: texto suelto, no receta .empty,
     igual que v1). .hr-status/las tarjetas KPI/.hr-vacation-legal: solo
     lectura, sin estados (mismo criterio que badge.css/kpi.css). Loading:
     lo cubre .hr-loading (feedback.css); los formularios usan disabled
     nativo en el submit mientras "Simulando…"/"Calculando…" (ya en hr.js).
   Cambios visibles: pesos intermedios de v1 (500/600/690/750/760) suben al
     escalón permitido más cercano (empate → sube: 750→800, 600→650,
     500→550); tamaños fuera de escala (10.5/11.5/12.5/14/15/16/17.5/18/19/
     20/22/23/25/27/28px…) al `--fs-*` más próximo (empate → sube); espaciado
     fuera del conjunto {2,4,6,8,10,12,14,16,20,24,28,32,40,48} sube/baja al
     escalón más próximo (empate → sube); radios 8-13px → var(--r-sm)/--r/
     --r-lg; las tres tiras de 3 KPIs (hr-kpis/-third-party-kpis/-vacation-
     kpis) eran casi-idénticas en v1 con paddings/gaps dispares → UNA receta;
     botones sin clase de .hr-row-actions/.hr-card-actions compartían
     selector con los reales .primary-action/.secondary-action en v1 (empate
     de especificidad accidental que en teoría los pisaba) → aquí se
     excluyen por :not() para que el botón clasificado siempre gane;
     .hr-third-party-table ya no recolorea .primary-action a ámbar (la fila
     `.needs-review` ya lo señala con su propio tinte — doble señal
     redundante en v1); #7e5c27 (texto de aviso ámbar) → var(--amber), misma
     receta que .hr-alert; fondo crema #fffdf8 de fila con revisión
     pendiente → color-mix con var(--amber); alturas de control puntuales
     (31/34px) → var(--control-h-compact); breakpoints propios de hr.css
     (1050/720) → 1100/680 canon.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── Raíz + resumen ──────────────────────────────────────────────────── */
  .hr-module { padding: 4px 0 48px; color: var(--ink); }

  .hr-hero {
    display: flex; align-items: center; justify-content: space-between;
    overflow: hidden; padding: 20px 24px;
    border: 1px solid var(--line); border-radius: var(--r);
    background: var(--surface-2);
  }
  .hr-hero h2 {
    margin: 4px 0 6px; font-family: var(--font-display); font-size: var(--fs-h2);
    font-weight: 400; letter-spacing: -.015em; color: var(--ink-2);
  }
  .hr-hero p:not(.eyebrow) { margin: 0; max-width: 660px; color: var(--muted); }
  .hr-hero-badge {
    display: flex; flex-direction: column; align-items: center;
    min-width: 152px; padding: 20px 24px;
    border: 1px solid color-mix(in srgb, var(--accent) 16%, transparent); border-radius: var(--r-lg);
    background: color-mix(in srgb, var(--surface) 78%, transparent);
  }
  .hr-hero-badge strong { font-family: var(--font-numeric); font-size: var(--fs-title); font-weight: 800; color: var(--accent); }
  .hr-hero-badge span { margin-top: 6px; font-size: var(--fs-small); color: var(--muted); }

  /* ── Tiras de 3 KPI: receta única (v1 la repetía 3 veces casi igual) ──── */
  .hr-kpis, .hr-third-party-kpis, .hr-vacation-kpis {
    display: grid; gap: 12px; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 14px 0;
  }
  .hr-kpis article, .hr-third-party-kpis article, .hr-vacation-kpis article {
    display: grid; gap: 6px; padding: 16px 20px;
    border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface);
  }
  :is(.hr-third-party-kpis, .hr-vacation-kpis) article.has-attention {
    border-color: color-mix(in srgb, var(--amber) 34%, transparent); background: var(--amber-soft);
  }
  .hr-kpis span, .hr-third-party-kpis span, .hr-vacation-kpis span {
    font-size: var(--fs-caption); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted);
  }
  .hr-kpis strong, .hr-third-party-kpis strong, .hr-vacation-kpis strong {
    font-family: var(--font-numeric); font-size: var(--fs-h2); font-weight: 800;
  }
  .hr-kpis small, .hr-third-party-kpis small, .hr-vacation-kpis small { font-size: var(--fs-caption); color: var(--muted-2); }

  .hr-stage { margin-top: 14px; }

  /* ── Cabecera de sección + selector inline ─────────────────────────────── */
  .hr-section-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 24px; border: 1px solid var(--line); border-radius: var(--r-lg) var(--r-lg) 0 0;
    background: var(--surface);
  }
  .hr-section-head h3, .hr-settings-card h3, .hr-legal-card h3 {
    margin: 4px 0; font-family: var(--font-numeric); font-size: var(--fs-h2); font-weight: 800;
  }
  .hr-section-head span { font-size: var(--fs-body); color: var(--muted); }
  .hr-head-actions, .hr-period-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
  .hr-inline-select {
    display: flex; align-items: center; gap: 8px; padding: 4px 8px 4px 12px;
    border: 1px solid var(--line); border-radius: var(--r);
  }
  .hr-inline-select span { font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; color: var(--muted); }
  .hr-inline-select select { padding: 7px 4px; border: 0; outline: 0; font-weight: 700; color: var(--ink); }

  /* ── Tabla ──────────────────────────────────────────────────────────────
     Botones sin clase de fila/tarjeta: :not() los saca de la receta ghost
     apenas hr.js les pone .primary-action/.secondary-action (ver cabecera:
     en v1 competían por especificidad con el botón real). */
  .hr-table-wrap { overflow: auto; border: 1px solid var(--line); border-radius: 0 0 var(--r-lg) var(--r-lg); background: var(--surface); }
  .hr-table { width: 100%; min-width: 920px; border-collapse: collapse; }
  .hr-table th {
    padding: 12px 16px; border-bottom: 1px solid var(--line);
    background: var(--surface-2); color: var(--muted-2);
    font-size: var(--fs-caption); letter-spacing: .07em; text-transform: uppercase; text-align: left;
  }
  .hr-table td { padding: 16px; border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
  .hr-table tr:last-child td { border-bottom: 0; }
  .hr-table tr:hover td { background: var(--surface-2); }
  .hr-table tr.is-muted { opacity: .58; }
  .hr-table td strong { display: block; font-size: var(--fs-body); }
  .hr-table td span:not(.hr-status) { display: block; margin-top: 2px; font-size: var(--fs-caption); color: var(--muted); }

  .hr-row-actions { text-align: right; white-space: nowrap; }
  .hr-row-actions button:not(.primary-action):not(.secondary-action),
  .hr-card-actions button:not(.primary-action):not(.secondary-action) {
    margin-left: 6px; padding: 8px 10px;
    border: 1px solid var(--line-2); border-radius: var(--r-sm);
    background: var(--surface); color: var(--ink-2);
    font-size: var(--fs-caption); font-weight: 700;
    transition: border-color var(--t), color var(--t);
  }
  .hr-row-actions button:not(.primary-action):not(.secondary-action):hover,
  .hr-card-actions button:not(.primary-action):not(.secondary-action):hover { border-color: var(--accent); color: var(--accent); }
  .hr-card-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: auto; }

  .hr-net-cell { font-size: var(--fs-body-lg); color: var(--green); }

  /* ── Píldora de estado (solo lectura, sin hover/focus/disabled) ────────── */
  .hr-status {
    --status-c: var(--muted);
    display: inline-flex; align-items: center;
    padding: 4px 8px; border-radius: 999px;
    background: color-mix(in srgb, var(--status-c) 10%, var(--surface));
    color: var(--status-c);
    font-size: var(--fs-caption); font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
  }
  .hr-status.approved, .hr-status.paid { --status-c: var(--green); }
  .hr-status.calculated { --status-c: var(--blue); }
  .hr-status.closed { --status-c: var(--muted); background: var(--surface-3); }
  .hr-status.draft { --status-c: var(--amber); }

  /* ── Tarjeta de período (Remuneraciones) ────────────────────────────────── */
  .hr-period-card { padding: 24px; border: 1px solid var(--line); border-bottom: 0; background: var(--surface); }
  .hr-period-title { display: flex; align-items: flex-start; justify-content: space-between; }
  .hr-period-title h4 { margin: 8px 0 2px; font-family: var(--font-numeric); font-size: var(--fs-h2); font-weight: 800; text-transform: capitalize; }
  .hr-period-title p { margin: 0; font-size: var(--fs-small); color: var(--muted); }
  .hr-period-metrics { display: grid; gap: 10px; grid-template-columns: repeat(4, minmax(0, 1fr)); margin-top: 20px; }
  .hr-period-metrics div { display: grid; gap: 4px; padding: 14px; border-radius: var(--r); background: var(--surface-2); }
  .hr-period-metrics span { font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; color: var(--muted); }
  .hr-period-metrics strong { font-size: var(--fs-body); }

  /* ── Cumplimiento (LRE/Previred) ────────────────────────────────────────── */
  .hr-compliance-hero {
    display: flex; align-items: center; justify-content: space-between;
    padding: 24px; border: 1px solid color-mix(in srgb, var(--amber) 30%, transparent); border-bottom: 0;
    background: linear-gradient(135deg, var(--amber-soft), var(--surface));
  }
  .hr-compliance-hero.is-complete {
    border-color: color-mix(in srgb, var(--green) 26%, transparent);
    background: linear-gradient(135deg, var(--green-soft), var(--surface));
  }
  .hr-compliance-hero h4 { margin: 8px 0 4px; font-family: var(--font-numeric); font-size: var(--fs-h2); font-weight: 800; text-transform: capitalize; }
  .hr-compliance-hero p { margin: 0; font-size: var(--fs-caption); color: var(--muted); }
  .hr-compliance-score { display: grid; gap: 4px; text-align: right; }
  .hr-compliance-score strong { font-family: var(--font-numeric); font-size: var(--fs-title); font-weight: 800; color: var(--accent); }
  .hr-compliance-score span { font-size: var(--fs-caption); text-transform: uppercase; color: var(--muted); }

  .hr-compliance-grid { display: grid; gap: 12px; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 14px 0; }
  .hr-compliance-card {
    display: flex; flex-direction: column; min-height: 255px; padding: 16px;
    border: 1px solid var(--line); border-top: 3px solid var(--amber-bright); border-radius: var(--r-lg);
    background: var(--surface);
  }
  .hr-compliance-card.is-done { border-top-color: var(--green); }
  .hr-compliance-card > div:first-child { display: flex; align-items: center; justify-content: space-between; }
  .hr-compliance-card > div:first-child small { font-size: var(--fs-caption); color: var(--muted); }
  .hr-compliance-card h4 { margin: 16px 0 6px; font-family: var(--font-numeric); font-size: var(--fs-h3); font-weight: 800; }
  .hr-compliance-card > p { margin: 0 0 12px; font-size: var(--fs-caption); line-height: 1.5; color: var(--muted); }
  .hr-compliance-card dl {
    display: grid; grid-template-columns: 1fr auto; row-gap: 6px; margin: 0 0 10px; padding: 10px;
    border-radius: var(--r-sm); background: var(--surface-2); font-size: var(--fs-caption);
  }
  .hr-compliance-card dt { color: var(--muted); }
  .hr-compliance-card dd { margin: 0; font-weight: 800; }
  .hr-compliance-card dd.is-zero { color: var(--green); }
  .hr-compliance-card dd.has-difference { color: var(--amber); }

  .hr-filing-proof { display: grid; gap: 2px; margin-bottom: 10px; padding-left: 10px; border-left: 2px solid var(--line-2); }
  .hr-filing-proof span, .hr-filing-proof small { font-size: var(--fs-caption); color: var(--muted); }
  .hr-filing-proof strong { font-size: var(--fs-caption); }

  .hr-delivery-section { margin-top: 14px; padding: 20px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
  .hr-delivery-section .hr-table-wrap { border-radius: var(--r); }
  .hr-compliance-disclaimer { margin: 12px 4px; font-size: var(--fs-caption); line-height: 1.5; color: var(--muted); }

  /* ── Terceros (novedades: CLA, licencias médicas) ───────────────────────── */
  .hr-third-party-sources { display: grid; gap: 12px; grid-template-columns: minmax(280px, .7fr) minmax(0, 1.3fr); margin-bottom: 14px; }
  .hr-source-workflow {
    display: flex; flex-direction: column; min-height: 235px; padding: 20px;
    border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface);
  }
  .hr-source-workflow.is-caja { border-color: var(--line-2); background: linear-gradient(145deg, var(--surface), color-mix(in srgb, var(--accent) 5%, var(--surface))); }
  .hr-source-workflow-head { display: flex; align-items: center; gap: 12px; }
  .hr-source-workflow-head > span {
    display: flex; flex: 0 0 42px; align-items: center; justify-content: center; height: 42px;
    border-radius: var(--r-sm); background: var(--ink); color: var(--surface);
    font-size: var(--fs-caption); font-weight: 800;
  }
  .hr-source-workflow-head h4 { margin: 2px 0 0; font-family: var(--font-numeric); font-size: var(--fs-h3); font-weight: 800; }
  .hr-source-workflow > p { font-size: var(--fs-caption); line-height: 1.55; color: var(--muted); }
  .hr-source-workflow > button {
    margin-top: auto; padding: 10px 12px; border: 0; border-radius: var(--r-sm);
    background: var(--accent); color: var(--surface); font-size: var(--fs-caption); font-weight: 800;
    transition: background var(--t);
  }
  .hr-source-workflow > button:hover { background: var(--brand); }

  .hr-license-channels { display: grid; gap: 8px; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-top: 16px; }
  .hr-license-channels button {
    display: grid; gap: 4px; min-height: 70px; padding: 12px; text-align: left;
    border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); color: var(--ink);
    transition: background var(--t), border-color var(--t);
  }
  .hr-license-channels button:hover { border-color: var(--line-2); background: var(--blue-soft); }
  .hr-license-channels strong { font-size: var(--fs-caption); }
  .hr-license-channels span { font-size: var(--fs-caption); overflow-wrap: anywhere; color: var(--muted); }

  .hr-inbox-state { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--line); }
  .hr-inbox-state > span { font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; color: var(--amber); }
  .hr-inbox-state.is-ready > span { color: var(--green); }
  .hr-inbox-state small { font-size: var(--fs-caption); overflow-wrap: anywhere; color: var(--muted); }

  .hr-third-party-ledger { padding: 20px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
  .hr-third-party-ledger .hr-table-wrap { border-radius: var(--r); }
  .hr-third-party-table tr.needs-review td { background: color-mix(in srgb, var(--amber) 5%, var(--surface)); }

  .hr-import-summary { display: grid; gap: 10px; grid-template-columns: repeat(3, minmax(0, 1fr)); padding: 24px 24px 0; }
  .hr-import-summary div { display: grid; gap: 4px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .hr-import-summary span { font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; color: var(--muted); }
  .hr-import-summary strong { font-family: var(--font-numeric); font-size: var(--fs-h2); font-weight: 800; }
  .hr-import-preview { margin: 16px 24px 0; max-height: 360px; border-radius: var(--r); }

  .hr-inbox-banner {
    display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 20px; padding: 12px 14px;
    border: 1px solid var(--line-2); border-radius: var(--r); background: color-mix(in srgb, var(--accent) 5%, var(--surface));
  }
  .hr-inbox-banner > div { display: grid; gap: 2px; }
  .hr-inbox-banner span { font-size: var(--fs-caption); font-weight: 800; text-transform: uppercase; color: var(--accent); }
  .hr-inbox-banner strong { font-size: var(--fs-small); }
  .hr-inbox-banner small { max-width: 310px; text-align: right; font-size: var(--fs-caption); color: var(--muted); }

  .hr-evidence-strip { display: grid; gap: 2px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); }
  .hr-evidence-strip span, .hr-evidence-strip small { font-size: var(--fs-caption); color: var(--muted); }
  .hr-evidence-strip strong { font-size: var(--fs-caption); overflow-wrap: anywhere; }

  .hr-third-party-applied {
    display: grid; gap: 4px; margin: -8px 0 20px; padding: 12px 14px;
    border: 1px solid color-mix(in srgb, var(--green) 26%, transparent); border-radius: var(--r);
    background: var(--green-soft);
  }
  .hr-third-party-applied strong { font-size: var(--fs-caption); color: var(--green); }
  .hr-third-party-applied span, .hr-third-party-applied small { font-size: var(--fs-caption); color: var(--green); }

  /* ── Vacaciones ─────────────────────────────────────────────────────────── */
  .hr-vacation-explainer {
    display: grid; gap: 4px; align-items: flex-start; margin: 14px 0; padding: 14px 16px;
    border: 1px solid var(--line-2); border-radius: var(--r-lg); background: color-mix(in srgb, var(--accent) 6%, var(--surface));
  }
  .hr-vacation-explainer strong { font-size: var(--fs-caption); color: var(--accent); }
  .hr-vacation-explainer span { font-size: var(--fs-caption); line-height: 1.5; color: var(--muted); }
  .hr-vacation-table td:nth-child(2) > strong { color: var(--green); }

  .hr-vacation-legal { display: grid; gap: 10px; grid-template-columns: repeat(4, minmax(0, 1fr)); margin-top: 14px; }
  .hr-vacation-legal div { display: grid; gap: 4px; padding: 14px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
  .hr-vacation-legal strong { font-size: var(--fs-caption); color: var(--accent); }
  .hr-vacation-legal span { font-size: var(--fs-caption); line-height: 1.45; color: var(--muted); }

  .hr-vacation-detail { padding: 24px 24px 28px; }
  .hr-vacation-detail > .hr-alert { margin: 0 0 12px; }
  .hr-vacation-balance { display: grid; gap: 10px; grid-template-columns: repeat(4, minmax(0, 1fr)); margin-bottom: 14px; }
  .hr-vacation-balance > div { display: grid; gap: 4px; padding: 14px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .hr-vacation-balance > div.is-primary { border-color: color-mix(in srgb, var(--green) 26%, transparent); background: var(--green-soft); }
  .hr-vacation-balance span { font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; color: var(--muted); }
  .hr-vacation-balance strong { font-family: var(--font-numeric); font-size: var(--fs-h3); font-weight: 800; }
  .hr-vacation-balance small { font-size: var(--fs-caption); line-height: 1.4; color: var(--muted); }

  .hr-vacation-panel { margin-top: 12px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); overflow: hidden; }
  .hr-vacation-panel > summary {
    display: flex; align-items: center; justify-content: space-between; padding: 14px 16px;
    list-style: none; cursor: pointer; font-size: var(--fs-caption); font-weight: 800;
  }
  .hr-vacation-panel > summary::-webkit-details-marker { display: none; }
  .hr-vacation-panel > summary::after { margin-left: 10px; content: "+"; font-size: var(--fs-h3); color: var(--accent); }
  .hr-vacation-panel[open] > summary::after { content: "−"; }
  .hr-vacation-panel > summary span { margin-left: auto; font-size: var(--fs-caption); font-weight: 650; color: var(--muted); }
  .hr-vacation-panel > .hr-form { padding: 16px; border-top: 1px solid var(--line); }
  .hr-vacation-panel.is-static { padding: 0; }
  .hr-vacation-panel-title { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; border-bottom: 1px solid var(--line); }
  .hr-vacation-panel-title strong { font-size: var(--fs-caption); }
  .hr-vacation-panel-title span { font-size: var(--fs-caption); color: var(--muted); }

  .hr-vacation-day-preview {
    display: grid; gap: 4px; margin: 12px 0; padding: 10px 12px;
    border: 1px solid var(--line-2); border-radius: var(--r-sm); background: color-mix(in srgb, var(--accent) 6%, var(--surface));
    color: var(--muted); font-size: var(--fs-caption);
  }
  .hr-vacation-day-preview strong { font-size: var(--fs-caption); color: var(--accent); }
  .hr-vacation-day-preview.has-error { border-color: color-mix(in srgb, var(--red) 24%, transparent); background: var(--red-soft); color: var(--red); }

  .hr-vacation-request-list { display: grid; }
  .hr-vacation-request-list article {
    display: grid; align-items: center; gap: 12px; grid-template-columns: minmax(0, 1fr) auto auto;
    padding: 12px 16px; border-bottom: 1px solid var(--line);
  }
  .hr-vacation-request-list article:last-child { border-bottom: 0; }
  .hr-vacation-request-list article > div:first-child { display: grid; gap: 4px; }
  .hr-vacation-request-list strong { font-size: var(--fs-caption); }
  .hr-vacation-request-list span:not(.hr-status), .hr-vacation-request-list small { font-size: var(--fs-caption); color: var(--muted); }
  .hr-vacation-empty { margin: 0; padding: 16px; font-size: var(--fs-caption); color: var(--muted); }

  .hr-vacation-adjustments, .hr-vacation-entitlements { display: grid; padding: 0 16px; border-top: 1px solid var(--line); }
  .hr-vacation-adjustments div, .hr-vacation-entitlements div { display: flex; align-items: center; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid var(--line); }
  .hr-vacation-adjustments div:last-child, .hr-vacation-entitlements div:last-child { border-bottom: 0; }
  .hr-vacation-adjustments strong, .hr-vacation-entitlements strong { font-size: var(--fs-caption); color: var(--green); }
  .hr-vacation-adjustments strong.is-negative { color: var(--red); }
  .hr-vacation-adjustments span, .hr-vacation-entitlements span, .hr-vacation-entitlements p { font-size: var(--fs-caption); color: var(--muted); }

  .hr-vacation-sources { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
  .hr-vacation-sources a {
    padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-sm);
    background: var(--surface-2); color: var(--accent); font-size: var(--fs-caption); font-weight: 700; text-decoration: none;
  }

  /* ── Configuración de empresa + marco legal ─────────────────────────────── */
  .hr-settings-grid { display: grid; gap: 14px; grid-template-columns: minmax(0, 1.45fr) minmax(300px, .75fr); }
  .hr-settings-card, .hr-legal-card { padding: 24px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
  .hr-card-head { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 20px; }
  .hr-card-head > span { font-size: var(--fs-caption); color: var(--muted); }
  .hr-settings-card > .hr-card-head { margin-bottom: 0; padding-bottom: 20px; border-bottom: 1px solid var(--line); }
  .hr-settings-card > .hr-card-head p:not(.eyebrow) { margin: 6px 0 0; font-size: var(--fs-caption); line-height: 1.45; color: var(--muted); }
  .hr-settings-section { padding: 24px 0; border-bottom: 1px solid var(--line); }
  .hr-settings-section-head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 16px; }
  .hr-settings-section-head > span {
    display: flex; flex: 0 0 24px; align-items: center; justify-content: center; height: 24px;
    border-radius: 50%; background: var(--blue-soft); color: var(--accent); font-size: var(--fs-caption); font-weight: 800;
  }
  .hr-settings-section-head h4 { margin: 2px 0 4px; font-family: var(--font-numeric); font-size: var(--fs-body-lg); font-weight: 800; }
  .hr-settings-section-head p { margin: 0; font-size: var(--fs-caption); color: var(--muted); }

  .hr-affiliation-note {
    display: flex; align-items: flex-start; gap: 8px; margin-top: 14px; padding: 10px 12px;
    border: 1px solid var(--line-2); border-radius: var(--r-sm); background: color-mix(in srgb, var(--accent) 4%, var(--surface));
    font-size: var(--fs-caption); line-height: 1.45; color: var(--muted);
  }
  .hr-affiliation-note strong { white-space: nowrap; color: var(--accent); }

  .hr-auto-code { display: grid; gap: 4px; min-height: 64px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); }
  .hr-auto-code > span { font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; color: var(--muted-2); }
  .hr-auto-code > strong { font-size: var(--fs-small); }
  .hr-auto-code > small { font-size: var(--fs-caption); color: var(--muted); }
  .hr-auto-code b { color: var(--accent); }

  .hr-legal-card { background: var(--surface-2); }
  .hr-legal-card > ul { display: grid; gap: 10px; padding-left: 20px; font-size: var(--fs-body); line-height: 1.45; color: var(--ink-2); }
  .hr-legal-card h4 { margin: 24px 0 10px; font-size: var(--fs-small); letter-spacing: .04em; text-transform: uppercase; }
  .hr-legal-note { margin-top: 20px; padding: 14px; border: 1px solid var(--line); border-left: 3px solid var(--amber-bright); border-radius: var(--r-sm); background: var(--surface); }
  .hr-legal-note strong { font-size: var(--fs-small); }
  .hr-legal-note p { margin: 6px 0 0; font-size: var(--fs-caption); line-height: 1.5; color: var(--muted); }
  .hr-source-list { display: grid; gap: 8px; }
  .hr-source-list a {
    display: flex; align-items: center; justify-content: space-between; padding: 10px 12px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
    color: var(--accent); font-size: var(--fs-caption); font-weight: 700; text-decoration: none;
  }

  /* ── Repaso de extracción (documento subido → ficha) ────────────────────── */
  .hr-extraction-review {
    display: grid; gap: 10px; margin-bottom: 20px; padding: 16px;
    border: 1px solid var(--line-2); border-radius: var(--r-lg);
    background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 6%, var(--surface)), color-mix(in srgb, var(--accent) 3%, var(--surface)));
  }
  .hr-extraction-review > div:first-child { display: grid; gap: 4px; }
  .hr-extraction-review > div:first-child > span { font-size: var(--fs-caption); font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--accent); }
  .hr-extraction-review > div:first-child strong { font-size: var(--fs-body); }
  .hr-extraction-review p { margin: 0; font-size: var(--fs-caption); color: var(--muted); }
  .hr-extraction-list { display: grid; gap: 4px; padding: 10px 12px; border-radius: var(--r-sm); background: color-mix(in srgb, var(--surface) 72%, transparent); font-size: var(--fs-caption); }
  .hr-extraction-list > strong { font-size: var(--fs-caption); color: var(--ink-2); }
  .hr-extraction-list span { color: var(--muted); }
  .hr-extraction-list.is-missing { border-left: 3px solid var(--amber); }
  .hr-extraction-list.is-ready { border-left: 3px solid var(--green); }

  .hr-automation-note { margin: 0 0 14px; }

  /* ── Formularios de fichas/modales ──────────────────────────────────────── */
  .hr-form { padding: 24px; }
  .hr-form-section { margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid var(--line); }
  .hr-form-section h3 { margin: 0 0 14px; font-family: var(--font-numeric); font-size: var(--fs-h3); font-weight: 800; }
  .hr-form-section-title { display: flex; align-items: center; justify-content: space-between; }
  .hr-form-section-title span { font-size: var(--fs-caption); color: var(--muted); }
  .hr-form-grid { display: grid; gap: 14px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hr-form-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .hr-field { display: grid; gap: 6px; }
  .hr-field-wide { grid-column: 1 / -1; }
  .hr-field > span { font-size: var(--fs-caption); font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
  .hr-field > small { font-size: var(--fs-caption); line-height: 1.4; color: var(--muted); }
  .hr-field input, .hr-field select, .hr-field textarea, .hr-settings-card select {
    box-sizing: border-box; width: 100%; min-height: var(--control-h); padding: 10px 12px;
    border: 1px solid var(--line-2); border-radius: var(--r-sm); background: var(--surface); color: var(--ink);
    font: 550 var(--fs-body)/1.3 var(--sans); outline: 0;
  }
  .hr-field textarea { resize: vertical; }
  .hr-field input:focus, .hr-field select:focus, .hr-field textarea:focus { border-color: var(--accent); box-shadow: var(--focus); }
  .hr-field.has-error input { border-color: var(--red); box-shadow: 0 0 0 3px color-mix(in srgb, var(--red) 11%, transparent); }
  .hr-field.has-error > small { font-weight: 650; color: var(--red); }
  .hr-field input[readonly] { cursor: default; background: var(--surface-2); color: var(--muted); }

  .hr-check-grid { display: grid; gap: 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 14px; }
  .hr-check { display: flex; align-items: flex-start; gap: 8px; font-size: var(--fs-caption); line-height: 1.45; color: var(--ink-2); }
  .hr-check input { margin-top: 1px; accent-color: var(--accent); }
  .hr-form-hint { margin: 10px 0 0; font-size: var(--fs-caption); color: var(--muted); }
  .hr-form-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; padding-top: 16px; }
  .hr-form-actions .hr-check { margin-right: auto; }

  .hr-smart-defaults {
    display: grid; gap: 4px; align-items: flex-start; margin-top: 14px; padding: 12px 14px;
    border: 1px solid color-mix(in srgb, var(--green) 26%, transparent); border-radius: var(--r);
    background: var(--green-soft);
  }
  .hr-smart-defaults-wide { margin: 0 0 14px; }
  .hr-smart-defaults strong { font-size: var(--fs-caption); color: var(--green); }
  .hr-smart-defaults span { font-size: var(--fs-caption); line-height: 1.45; color: var(--green); }

  .hr-guided-details { margin-top: 14px; padding: 0 14px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .hr-guided-details summary { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; list-style: none; cursor: pointer; }
  .hr-guided-details summary::-webkit-details-marker { display: none; }
  .hr-guided-details summary span { font-size: var(--fs-caption); font-weight: 800; color: var(--ink); }
  .hr-guided-details summary small { font-size: var(--fs-caption); font-weight: 650; color: var(--muted); }
  .hr-guided-details summary::after { margin-left: 10px; content: "+"; font-size: var(--fs-h3); font-weight: 650; color: var(--accent); }
  .hr-guided-details[open] summary::after { content: "−"; }
  .hr-guided-details > .hr-form-grid, .hr-guided-details > .hr-check-grid { padding: 14px 0; border-top: 1px solid var(--line); }
  .hr-guided-details > .hr-check-grid { margin-top: 0; }
  .hr-guided-details > .hr-alert { margin: 0 0 14px; }

  .hr-history { margin-bottom: 14px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .hr-history summary { font-size: var(--fs-small); font-weight: 700; cursor: pointer; }
  .hr-history div { display: flex; justify-content: space-between; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); font-size: var(--fs-caption); }
  .hr-history span { color: var(--muted); }

  .hr-worker-strip { display: grid; gap: 8px; grid-template-columns: repeat(3, 1fr); margin-bottom: 20px; }
  .hr-worker-strip div { display: grid; gap: 4px; padding: 12px 14px; border-radius: var(--r-sm); background: var(--surface-2); }
  .hr-worker-strip span { font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; color: var(--muted); }
  .hr-worker-strip strong { font-size: var(--fs-small); }

  .hr-form-subgrid { margin-top: 14px; padding: 12px; border-radius: var(--r); background: var(--surface-2); }
  .hr-variable-head { display: flex; align-items: center; justify-content: space-between; margin-top: 16px; padding-top: 14px; border-top: 1px dashed var(--line-2); }
  .hr-variable-head > div { display: grid; gap: 2px; }
  .hr-variable-head strong { font-size: var(--fs-caption); }
  .hr-variable-head span { font-size: var(--fs-caption); color: var(--muted); }
  .hr-variable-head button { padding: 8px 10px; border: 1px solid var(--line-2); border-radius: var(--r-sm); background: var(--surface); color: var(--accent); font-size: var(--fs-caption); font-weight: 800; }
  .hr-variable-list { display: grid; gap: 8px; margin-top: 10px; }
  .hr-variable-row {
    display: grid; align-items: end; gap: 8px; grid-template-columns: .8fr .8fr 1.1fr 1.4fr .7fr 30px;
    padding: 10px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2);
  }
  .hr-variable-row label { display: grid; gap: 4px; }
  .hr-variable-row label > span { font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; color: var(--muted); }
  .hr-variable-row input, .hr-variable-row select {
    box-sizing: border-box; width: 100%; min-height: var(--control-h-compact); padding: 6px 8px;
    border: 1px solid var(--line-2); border-radius: var(--r-sm); font-size: var(--fs-caption);
  }
  .hr-variable-row > button {
    height: var(--control-h-compact); border: 1px solid color-mix(in srgb, var(--red) 24%, transparent);
    border-radius: var(--r-sm); background: var(--surface); color: var(--red); font-size: var(--fs-h3);
  }

  /* ── Cálculo histórico de sueldos ──────────────────────────────────────── */
  .hr-historical-rules { display: grid; gap: 6px; margin: 16px 0; padding-left: 20px; font-size: var(--fs-caption); line-height: 1.45; color: var(--muted); }
  .hr-historical-explainer {
    display: grid; gap: 4px; align-items: flex-start; margin: 14px 0; padding: 12px 14px;
    border: 1px solid var(--line-2); border-radius: var(--r); background: color-mix(in srgb, var(--accent) 6%, var(--surface));
  }
  .hr-historical-explainer strong { font-size: var(--fs-caption); color: var(--accent); }
  .hr-historical-explainer span { font-size: var(--fs-caption); line-height: 1.45; color: var(--muted); }

  .hr-historical-summary, .hr-historical-result { display: grid; gap: 10px; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 20px 0 12px; }
  .hr-historical-summary div, .hr-historical-result div { display: grid; gap: 4px; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .hr-historical-summary span, .hr-historical-result span { font-size: var(--fs-caption); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
  .hr-historical-summary strong, .hr-historical-result strong { font-family: var(--font-numeric); font-size: var(--fs-h3); font-weight: 800; }

  .hr-historical-table-wrap { max-height: 310px; overflow: auto; border: 1px solid var(--line); border-radius: var(--r); }
  .hr-historical-table { width: 100%; min-width: 760px; border-collapse: collapse; }
  .hr-historical-table th {
    position: sticky; top: 0; padding: 10px 12px; background: var(--surface-2); color: var(--muted-2);
    font-size: var(--fs-caption); letter-spacing: .05em; text-transform: uppercase; text-align: left;
  }
  .hr-historical-table td { padding: 10px 12px; border-top: 1px solid var(--line); font-size: var(--fs-caption); vertical-align: middle; }
  .hr-historical-table td > span:not(.hr-status), .hr-historical-table td > small { display: block; margin-top: 2px; font-size: var(--fs-caption); color: var(--muted); }
  .hr-historical-empty { margin-top: 12px; }

  .hr-historical-review-details {
    margin-top: 12px; padding: 0 14px; border: 1px solid color-mix(in srgb, var(--amber) 30%, transparent); border-radius: var(--r);
    background: var(--amber-soft);
  }
  .hr-historical-review-details summary { padding: 12px 0; font-size: var(--fs-caption); font-weight: 800; color: var(--amber); cursor: pointer; }
  .hr-historical-review-details > div { display: grid; gap: 8px; padding: 10px 0 12px; border-top: 1px solid color-mix(in srgb, var(--amber) 24%, transparent); }
  .hr-historical-review-details article { display: grid; gap: 4px; padding: 10px 12px; border-radius: var(--r-sm); background: var(--surface); }
  .hr-historical-review-details article strong { font-size: var(--fs-caption); }
  .hr-historical-review-details article span { font-size: var(--fs-caption); text-transform: capitalize; color: var(--muted); }
  .hr-historical-review-details article p { margin: 2px 0 0; font-size: var(--fs-caption); line-height: 1.4; color: var(--amber); }

  .hr-historical-minimum-details {
    margin-top: 12px; padding: 0 14px; border: 1px solid color-mix(in srgb, var(--green) 26%, transparent); border-radius: var(--r);
    background: var(--green-soft);
  }
  .hr-historical-minimum-details summary { padding: 12px 0; font-size: var(--fs-caption); font-weight: 800; color: var(--green); cursor: pointer; }
  .hr-historical-minimum-details > div { display: grid; gap: 8px; padding: 10px 0 12px; border-top: 1px solid color-mix(in srgb, var(--green) 22%, transparent); }
  .hr-historical-minimum-details article { display: grid; gap: 4px; padding: 10px 12px; border-radius: var(--r-sm); background: var(--surface); }
  .hr-historical-minimum-details article strong { font-size: var(--fs-caption); }
  .hr-historical-minimum-details article span { font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; color: var(--green); }
  .hr-historical-minimum-details article p { margin: 2px 0 0; font-size: var(--fs-caption); line-height: 1.55; text-transform: capitalize; color: var(--green); }

  .hr-history-months { margin-top: 12px; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
  .hr-history-months div { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; border-bottom: 1px solid var(--line); font-size: var(--fs-caption); }
  .hr-history-months div:last-child { border-bottom: 0; }
  .hr-history-months span { text-align: right; color: var(--muted); }

  /* ── Exportación (LRE/Previred) y constancia de entrega ─────────────────── */
  .hr-export-result { padding: 24px 24px 0; }
  .hr-export-summary {
    display: grid; gap: 6px; padding: 16px; border: 1px solid color-mix(in srgb, var(--green) 26%, transparent);
    border-radius: var(--r-lg); background: var(--green-soft);
  }
  .hr-export-result.has-errors .hr-export-summary { border-color: color-mix(in srgb, var(--amber) 34%, transparent); background: var(--amber-soft); }
  .hr-export-summary strong { font-size: var(--fs-body-lg); }
  .hr-export-summary span, .hr-export-summary small { font-size: var(--fs-caption); overflow-wrap: anywhere; color: var(--muted); }
  .hr-export-messages {
    margin-top: 12px; padding: 12px 16px; border: 1px solid color-mix(in srgb, var(--red) 24%, transparent);
    border-radius: var(--r); background: var(--red-soft); color: var(--red);
  }
  .hr-export-messages.is-warning { border-color: color-mix(in srgb, var(--amber) 34%, transparent); background: var(--amber-soft); color: var(--amber); }
  .hr-export-messages strong { font-size: var(--fs-caption); text-transform: uppercase; }
  .hr-export-messages ul { margin: 8px 0 0; padding-left: 20px; font-size: var(--fs-caption); line-height: 1.5; }
  .hr-delivery-history { margin: 14px 24px 0; padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
  .hr-delivery-history summary { font-size: var(--fs-caption); font-weight: 800; cursor: pointer; }
  .hr-delivery-history div { display: flex; justify-content: space-between; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); font-size: var(--fs-caption); }
  .hr-delivery-history span { color: var(--muted); }

  /* ── Simuladores (liquidación, finiquito, vacaciones) ───────────────────── */
  .hr-sim-picker { display: grid; gap: 12px; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 14px 0; }
  .hr-sim-picker button {
    display: grid; gap: 4px; padding: 16px 20px; text-align: left;
    border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface);
    transition: border-color var(--t), box-shadow var(--t), transform var(--t);
  }
  .hr-sim-picker button:hover { border-color: var(--line-2); transform: translateY(-1px); }
  .hr-sim-picker button.active { border-color: var(--accent); box-shadow: 0 0 0 3px var(--blue-soft); }
  .hr-sim-picker strong { font-family: var(--font-numeric); font-size: var(--fs-h3); font-weight: 800; }
  .hr-sim-picker button.active strong { color: var(--accent); }
  .hr-sim-picker span { font-size: var(--fs-caption); line-height: 1.45; color: var(--muted); }

  .hr-sim-workspace { display: grid; align-items: start; gap: 14px; grid-template-columns: minmax(300px, 360px) minmax(0, 1fr); }
  .hr-sim-form-panel { position: sticky; top: 76px; padding: 20px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
  .hr-sim-fields { display: grid; gap: 14px; }
  .hr-sim-field { display: grid; gap: 6px; }
  .hr-sim-field > span, .hr-sim-grid label > span { font-size: var(--fs-caption); font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
  .hr-sim-field em { font-style: normal; font-weight: 650; text-transform: none; color: var(--muted-2); }
  .hr-sim-field small { font-size: var(--fs-caption); line-height: 1.45; color: var(--muted-2); }
  .hr-sim-field input, .hr-sim-field select, .hr-sim-grid input {
    width: 100%; padding: 10px 12px; border: 1px solid var(--line-2); border-radius: var(--r-sm);
    font: 650 var(--fs-body)/1.2 var(--sans); color: var(--ink);
  }
  .hr-sim-field input:focus, .hr-sim-field select:focus, .hr-sim-grid input:focus { border-color: var(--accent); outline: 2px solid var(--blue-soft); }
  .hr-sim-grid { display: grid; gap: 10px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hr-sim-grid label { display: grid; gap: 6px; }
  .hr-sim-check { display: flex; align-items: center; gap: 8px; font-size: var(--fs-small); font-weight: 650; color: var(--ink-2); }
  .hr-sim-disclaimer { margin: 16px 0 0; padding-top: 12px; border-top: 1px dashed var(--line); font-size: var(--fs-caption); line-height: 1.5; color: var(--muted-2); }

  .hr-sim-person-mode { display: grid; gap: 4px; grid-template-columns: 1fr 1fr; padding: 4px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .hr-sim-person-mode button { padding: 10px; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--muted); font-size: var(--fs-small); font-weight: 800; }
  .hr-sim-person-mode button.active { background: var(--surface); box-shadow: var(--shadow-xs); color: var(--accent); }

  .hr-contract-doc-toggle { display: grid; gap: 4px; grid-template-columns: 1fr 1fr; margin-bottom: 16px; padding: 4px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .hr-contract-doc-toggle button { padding: 10px 12px; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--muted); font-size: var(--fs-body); font-weight: 800; }
  .hr-contract-doc-toggle button.active { background: var(--surface); box-shadow: var(--shadow-xs); color: var(--accent); }
  .hr-contract-doc-toggle button:disabled { cursor: not-allowed; opacity: .5; }
  .hr-contract-alt-times { display: grid; gap: 6px; grid-template-columns: 1fr 1fr; }
  .hr-contract-alt-times input { width: 100%; padding: 8px 10px; border: 1px solid var(--line-2); border-radius: var(--r-sm); }
  .hr-contract-doc-summary { margin-top: 14px; padding: 12px 14px; border-left: 3px solid var(--accent); border-radius: var(--r); background: var(--surface-2); }
  .hr-contract-doc-summary strong { font-size: var(--fs-caption); }
  .hr-contract-doc-summary span { font-size: var(--fs-caption); line-height: 1.55; color: var(--muted); }
  .hr-sim-inline-help { margin-top: -4px; font-size: var(--fs-caption); line-height: 1.5; color: var(--muted-2); }

  .hr-sim-result { display: grid; gap: 14px; min-height: 320px; padding: 24px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
  .hr-sim-placeholder { display: grid; align-content: center; justify-items: center; gap: 10px; min-height: 276px; text-align: center; }
  .hr-sim-placeholder span { position: relative; width: 42px; height: 42px; border-radius: 50%; background: var(--blue-soft); }
  .hr-sim-placeholder span::after { content: "≈"; position: absolute; inset: 0; font: 800 var(--fs-h2)/42px var(--sans); color: var(--accent); }
  .hr-sim-placeholder p { max-width: 420px; margin: 0; font-size: var(--fs-body); line-height: 1.6; color: var(--muted); }

  .hr-sim-result-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
  .hr-sim-result-actions { display: flex; align-items: center; flex-shrink: 0; gap: 8px; }
  .hr-sim-result-head h4 { margin: 4px 0 4px; font-family: var(--font-numeric); font-size: var(--fs-h2); font-weight: 800; }
  .hr-sim-result-head span:not(.hr-status) { font-size: var(--fs-caption); color: var(--muted); }

  .hr-sim-tags { display: flex; flex-wrap: wrap; gap: 6px; }
  .hr-sim-tag { padding: 6px 10px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface-2); color: var(--muted); font-size: var(--fs-caption); font-weight: 700; }
  .hr-sim-tag.is-good { border-color: color-mix(in srgb, var(--green) 26%, transparent); background: var(--green-soft); color: var(--green); }
  .hr-sim-tag.is-alert { border-color: color-mix(in srgb, var(--amber) 34%, transparent); background: var(--amber-soft); color: var(--amber); }

  .hr-sim-kpis { display: grid; gap: 10px; grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .hr-sim-kpis article { display: grid; gap: 4px; padding: 14px 16px; border-radius: var(--r-lg); background: var(--surface-2); }
  .hr-sim-kpis article.is-primary { background: var(--blue-soft); }
  .hr-sim-kpis span { font-size: var(--fs-caption); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
  .hr-sim-kpis strong { font-family: var(--font-numeric); font-size: var(--fs-h3); font-weight: 800; }
  .hr-sim-kpis article.is-primary strong { color: var(--accent); }
  .hr-sim-kpis small { font-size: var(--fs-caption); color: var(--muted-2); }

  .hr-sim-columns { display: grid; gap: 14px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .hr-sim-columns h5, .hr-sim-employer h5 { margin: 0 0 8px; font-size: var(--fs-caption); font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--muted); }
  .hr-sim-items { width: 100%; border-collapse: collapse; }
  .hr-sim-items td { padding: 10px 4px; border-bottom: 1px solid var(--line-soft); font-size: var(--fs-small); vertical-align: top; }
  .hr-sim-items tr:last-child td { border-bottom: 0; }
  .hr-sim-items td:last-child { font-weight: 800; text-align: right; white-space: nowrap; }
  .hr-sim-items td.is-negative { color: var(--red); }
  .hr-sim-items td strong { display: block; font-size: var(--fs-small); font-weight: 650; }
  .hr-sim-items td span { display: block; margin-top: 2px; font-size: var(--fs-caption); color: var(--muted-2); }
  .hr-sim-none { margin: 0; font-size: var(--fs-caption); color: var(--muted); }

  .hr-sim-note { padding: 12px 14px; border-radius: var(--r); background: var(--surface-2); font-size: var(--fs-caption); line-height: 1.55; color: var(--muted); }
  .hr-sim-warnings { padding: 14px 16px; border: 1px solid color-mix(in srgb, var(--amber) 34%, transparent); border-radius: var(--r-lg); background: var(--amber-soft); }
  .hr-sim-warnings strong { display: block; margin-bottom: 6px; font-size: var(--fs-caption); color: var(--amber); }
  .hr-sim-warnings ul { margin: 0; padding-left: 16px; font-size: var(--fs-caption); line-height: 1.55; color: var(--amber); }
  .hr-sim-warnings li + li { margin-top: 4px; }

  .hr-sim-base-card { display: grid; gap: 4px; padding: 14px 16px; border-left: 3px solid var(--accent); border-radius: var(--r); background: var(--surface-2); }
  .hr-sim-base-card span { font-size: var(--fs-caption); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
  .hr-sim-base-card strong { font-family: var(--font-numeric); font-size: var(--fs-h2); font-weight: 800; }
  .hr-sim-base-card small { font-size: var(--fs-caption); color: var(--muted-2); }

  .hr-sim-total-card {
    display: grid; align-items: baseline; gap: 4px; padding: 16px 20px;
    border: 1px solid var(--line-2); border-radius: var(--r-lg);
    background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 6%, var(--surface)), var(--surface));
  }
  .hr-sim-total-card span { font-size: var(--fs-caption); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); }
  .hr-sim-total-card strong { margin-left: 10px; font-family: var(--font-numeric); font-size: var(--fs-title); font-weight: 800; color: var(--accent); }
  .hr-sim-total-card small { font-size: var(--fs-caption); color: var(--muted); }
  .hr-sim-legal summary { font-size: var(--fs-small); font-weight: 700; color: var(--accent); cursor: pointer; }
  .hr-sim-legal ul { margin: 10px 0 0; padding-left: 16px; font-size: var(--fs-caption); line-height: 1.6; color: var(--muted); }
  .hr-sim-hypothetical { padding: 12px 16px; border: 1px solid color-mix(in srgb, var(--green) 26%, transparent); border-radius: var(--r-lg); background: var(--green-soft); color: var(--green); font-size: var(--fs-small); }
  .hr-sim-hypothetical.is-alert { border-color: color-mix(in srgb, var(--amber) 34%, transparent); background: var(--amber-soft); color: var(--amber); }
  .hr-sim-hypothetical strong { display: block; margin-bottom: 4px; }

  .hr-sim-chart { display: flex; align-items: end; gap: 6px; min-height: 150px; padding: 16px 14px 10px; border-radius: var(--r-lg); background: var(--surface-2); overflow-x: auto; }
  .hr-sim-bar { display: grid; flex: 1 1 0; grid-template-rows: 96px auto auto; gap: 4px; justify-items: center; min-width: 34px; }
  .hr-sim-bar i {
    align-self: end; width: 62%; min-height: 5px; display: block; border-radius: 5px 5px 2px 2px;
    background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 55%, var(--surface)), var(--accent));
  }
  .hr-sim-bar i.is-negative { background: linear-gradient(180deg, color-mix(in srgb, var(--red) 45%, var(--surface)), var(--red)); }
  .hr-sim-bar b { font-size: var(--fs-caption); font-weight: 800; }
  .hr-sim-bar span { font-size: var(--fs-caption); color: var(--muted-2); }

  /* ── Responsive (v1: 1050/720 propios de hr.css → 1100/680 canon) ──────── */
  @media (max-width: 1100px) {
    .hr-period-metrics { grid-template-columns: repeat(2, 1fr); }
    .hr-sim-workspace { grid-template-columns: 1fr; }
    .hr-sim-form-panel { position: static; }
    .hr-sim-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .hr-settings-grid { grid-template-columns: 1fr; }
    .hr-compliance-grid { grid-template-columns: repeat(2, 1fr); }
    .hr-third-party-sources { grid-template-columns: 1fr; }
    .hr-vacation-legal, .hr-vacation-balance { grid-template-columns: repeat(2, 1fr); }
  }

  @media (max-width: 680px) {
    .hr-module { padding-bottom: 24px; }
    .hr-hero { flex-direction: column; align-items: flex-start; gap: 20px; padding: 24px; }
    .hr-hero-badge { flex-direction: row; align-items: baseline; gap: 8px; min-width: 0; }
    .hr-kpis, .hr-third-party-kpis, .hr-vacation-kpis,
    .hr-license-channels, .hr-import-summary { grid-template-columns: 1fr; }
    .hr-section-head, .hr-period-title { flex-direction: column; align-items: stretch; gap: 14px; }
    .hr-head-actions { flex-direction: column; align-items: stretch; }
    .hr-inline-select { justify-content: space-between; }
    .hr-period-metrics, .hr-form-grid, .hr-form-grid-3, .hr-check-grid, .hr-worker-strip,
    .hr-historical-summary, .hr-historical-result, .hr-compliance-grid, .hr-variable-row { grid-template-columns: 1fr; }
    .hr-compliance-hero { flex-direction: column; align-items: stretch; }
    .hr-settings-card > .hr-card-head { gap: 10px; }
    .hr-settings-card > .hr-card-head > span { display: none; }
    .hr-affiliation-note { display: grid; }
    .hr-compliance-score { text-align: left; }
    .hr-vacation-legal, .hr-vacation-balance { grid-template-columns: 1fr; }
    .hr-vacation-request-list article { align-items: flex-start; grid-template-columns: 1fr; }
    .hr-sim-picker { grid-template-columns: 1fr; }
    .hr-sim-workspace { grid-template-columns: 1fr; }
    .hr-sim-form-panel { position: static; }
    .hr-sim-kpis, .hr-sim-columns, .hr-sim-grid { grid-template-columns: 1fr; }
  }
}

/* líneas: ~520 de presupuesto — excede a propósito (isla RRHH completa: 18
   familias .hr-* con sus estados, no una vista geométrica; ver cabecera del
   archivo, mismo criterio que toolbar.css/feedback.css/08-print.css) */
/* ══ kpi — tiras de métricas ══════════════════════════════════════════════
   Cubre: .book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
          .consumption-kpis, .inv-summary-strip, .recipe-technical-kpis,
          .supplier-detail-kpis, .annex-kpi-grid, .annex-match-summary,
          .coffee-quote-kpis, .coffee-catalog-summary, .coffee-calendar-summary,
          .event-hub-steps, .run-list, .overview-kpi, .overview-kpi-grid,
          .accounting-kpi-grid, .settings-kpi-row, .finance-reading-grid,
          .finance-support-grid, .cashflow-kpi-grid,
          variantes .accent/.warn/.attention/.bad y .overview-kpi--{tono}
   Fuente v1: styles.css ~L3775-3820, ~L4190-4465, ~L5548-5915, ~L6600-8030,
          ~L11440-11470, ~L12440-12545 (bases por módulo) fusionadas con
          ~L12810-13230 (v117 metric cards + ciclo --kpi-accent nth-child),
          ~L13359-13496 (canon: tarjeta blanca, filete superior, cifra
          numeric tabular, entrada escalonada) y ~L13609 (v120 auto-fit).
   Muertas (sin emisor, verificado con grep en app.js/index.php):
          .report-kpis (el DOM usa .report-kpi-strip), .daily-summary,
          .overview-spark, .overview-money-total — no se portan.
   Estados: hover (lift + filete engrosado). focus-visible/active/disabled/
          loading no aplican: tarjetas de solo lectura, sin foco. El vacío lo
          pinta JS con .empty (componente aparte); el dato ausente llega como
          "—" dentro de <strong>.
   Cambios visibles documentados: cifra unificada a 21px (v1 variaba 13-23px
          por tira); captions en caja alta en todas las tiras (v1 solo en
          algunas); grillas fijas de 3/4/6 columnas consolidadas al auto-fit
          de v120.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── Contenedor: grilla auto-fit (v120) ─────────────────────────────── */
  .book-summary,
  .sales-summary,
  .report-kpi-strip,
  .mom-kpis,
  .consumption-kpis,
  .inv-summary-strip,
  .recipe-technical-kpis,
  .supplier-detail-kpis,
  .annex-kpi-grid,
  .coffee-quote-kpis,
  .coffee-catalog-summary,
  .coffee-calendar-summary,
  .event-hub-steps,
  .run-list,
  .overview-kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px;
  }

  /* Resumen de homologación del anexo: columna única dentro del aside */
  .annex-match-summary {
    display: grid;
    gap: 8px;
  }

  /* Ritmo vertical heredado de v1 (la vista puede reposicionar) */
  .book-summary,
  .sales-summary { margin-top: 16px; }
  .mom-kpis { margin: 12px 0; }
  .consumption-kpis { margin-top: 14px; }
  .recipe-technical-kpis { margin: 16px 0; }
  .run-list { margin: 16px 0 0; }
  .annex-kpi-grid { margin-bottom: 16px; }
  .coffee-catalog-summary { margin: 8px 0 12px; }

  /* ── LA tarjeta KPI (receta única) ──────────────────────────────────── */
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .annex-match-summary, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps) > article,
  :is(.coffee-quote-kpis, .run-list, .inv-summary-strip) > div,
  .overview-kpi {
    --kpi-accent: var(--blue);
    position: relative;
    min-width: 0;
    min-height: 72px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 16px;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-xs);
    text-align: center;
    overflow: hidden;
    transition: transform var(--t), box-shadow var(--t), border-color var(--t);
  }

  /* Filete superior de acento, ancho completo */
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .annex-match-summary, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps) > article::after,
  :is(.coffee-quote-kpis, .run-list, .inv-summary-strip) > div::after,
  .overview-kpi::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--kpi-accent);
    opacity: .92;
    transition: height var(--t), opacity var(--t);
  }

  /* hover: leve despegue; el filete se engrosa */
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .annex-match-summary, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps) > article:hover,
  :is(.coffee-quote-kpis, .run-list, .inv-summary-strip) > div:hover,
  .overview-kpi:hover {
    transform: translateY(-2px);
    border-color: var(--line-2);
    box-shadow: var(--shadow-sm);
  }

  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .annex-match-summary, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps) > article:hover::after,
  :is(.coffee-quote-kpis, .run-list, .inv-summary-strip) > div:hover::after,
  .overview-kpi:hover::after {
    height: 4px;
    opacity: 1;
  }

  /* ── Ciclo de acentos por posición (como v1) ────────────────────────────
     Gana a la declaración por defecto de la tarjeta por especificidad
     (0,2,0 > 0,1,1). No incluye .overview-kpi-grid: ahí el tono lo decide
     el modificador que emite JS. */
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .coffee-quote-kpis, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps, .run-list,
      .inv-summary-strip) > :nth-child(1) { --kpi-accent: var(--green); }
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .coffee-quote-kpis, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps, .run-list,
      .inv-summary-strip) > :nth-child(2) { --kpi-accent: var(--blue); }
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .coffee-quote-kpis, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps, .run-list,
      .inv-summary-strip) > :nth-child(3) { --kpi-accent: var(--amber); }
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .coffee-quote-kpis, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps, .run-list,
      .inv-summary-strip) > :nth-child(4) { --kpi-accent: var(--teal); }
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .coffee-quote-kpis, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps, .run-list,
      .inv-summary-strip) > :nth-child(5) { --kpi-accent: var(--violet); }
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .coffee-quote-kpis, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps, .run-list,
      .inv-summary-strip) > :nth-child(6) { --kpi-accent: var(--rose); }

  /* ── Tonos semánticos que setea JS (setKpiTone) — después del ciclo
        posicional para ganarle por orden a igual especificidad ─────────── */
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis) > .accent { --kpi-accent: var(--green); }
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis) > :is(.warn, .attention) { --kpi-accent: var(--amber); }
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis) > .bad { --kpi-accent: var(--red); }

  /* ── Tipografía interna ─────────────────────────────────────────────── */
  /* Detalle (tercera línea): span de book/sales/report/mom/hub/overview… */
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .annex-match-summary, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps, .overview-kpi) span {
    width: 100%;
    margin: 0;
    color: var(--muted-2);
    font-size: var(--fs-caption);
    font-weight: 550;
    line-height: 1.25;
    text-align: center;
    white-space: normal;
    text-wrap: balance;
  }

  /* Caption caja alta: small/dt en todas las tiras; en coffee-quote-kpis e
     inv-summary-strip el rótulo llega como <span> (markup span+strong) */
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .annex-match-summary, .coffee-catalog-summary,
      .coffee-calendar-summary, .event-hub-steps, .run-list,
      .overview-kpi) :is(small, dt),
  :is(.coffee-quote-kpis, .inv-summary-strip) > div > span {
    width: 100%;
    margin: 0;
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 650;
    letter-spacing: .07em;
    text-transform: uppercase;
    line-height: 1.2;
    text-align: center;
    white-space: normal;
    text-wrap: balance;
  }

  /* Cifra protagonista: sans tight tabular (el <dd> del run-list incluido).
     La cifra negativa hereda tinta: el signo basta, sin rojo automático. */
  :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
      .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
      .annex-kpi-grid, .annex-match-summary, .coffee-quote-kpis,
      .coffee-catalog-summary, .coffee-calendar-summary, .event-hub-steps,
      .run-list, .inv-summary-strip, .overview-kpi) :is(strong, dd) {
    width: 100%;
    margin: 0;
    color: var(--ink);
    font-family: var(--font-numeric);
    font-size: var(--fs-h2);
    font-weight: 800;
    line-height: 1.05;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    text-align: center;
    text-wrap: balance;
  }

  /* ── Variante grande: KPI de Proyección/Salas ───────────────────────── */
  .overview-kpi { min-height: 132px; }

  .overview-kpi > div {
    min-width: 0;
    display: grid;
    justify-items: center;
    gap: 6px;
  }

  .overview-kpi strong { font-size: var(--fs-title); }

  /* Tono por modificador (JS emite blue/green/amber; blue = defecto) */
  .overview-kpi--green { --kpi-accent: var(--green); }
  .overview-kpi--teal  { --kpi-accent: var(--teal); }
  .overview-kpi--amber { --kpi-accent: var(--amber); }
  .overview-kpi--red   { --kpi-accent: var(--red); }

  /* ── Métricas financieras y de configuración ────────────────────────
     Comparten anatomía small/strong/span, pero mantienen fondos serenos
     sin el filete animado de las tiras protagonistas. */
  .accounting-kpi-grid,
  .settings-kpi-row,
  .finance-reading-grid,
  .finance-support-grid,
  .cashflow-kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px;
  }
  .accounting-kpi-grid { margin-bottom: 16px; }
  .finance-support-grid { margin-top: 16px; }

  :is(.accounting-kpi-grid, .settings-kpi-row, .finance-support-grid,
      .cashflow-kpi-grid) > article,
  .finance-reading-grid > div {
    display: grid;
    gap: 4px;
    min-width: 0;
    padding: 14px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-2);
  }
  :is(.accounting-kpi-grid, .settings-kpi-row) > article { background: var(--surface); }
  .finance-support-grid > article.is-tax {
    border-color: color-mix(in srgb, var(--blue) 24%, transparent);
    background: var(--blue-soft);
  }

  :is(.accounting-kpi-grid, .settings-kpi-row, .finance-reading-grid,
      .finance-support-grid, .cashflow-kpi-grid) :is(small, span) {
    color: var(--muted);
    font-size: var(--fs-caption);
    line-height: 1.35;
  }
  :is(.accounting-kpi-grid, .settings-kpi-row, .finance-reading-grid,
      .finance-support-grid, .cashflow-kpi-grid) small {
    font-weight: 650;
    letter-spacing: .06em;
    text-transform: uppercase;
  }
  :is(.accounting-kpi-grid, .settings-kpi-row, .finance-reading-grid,
      .finance-support-grid, .cashflow-kpi-grid) strong {
    color: var(--ink);
    font-family: var(--font-numeric);
    font-size: var(--fs-h2);
    font-weight: 800;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
  }

  /* Estas tiras duplican exactamente los KPI promovidos al page-header. */
  #view-book .book-summary,
  #view-sales .sales-summary,
  #view-menu #menuKpis,
  #view-reconciliation .book-summary,
  #view-mom #momKpis,
  #view-reports #reportKpiStrip,
  #view-consumption #consumptionKpis,
  #view-inventory .inv-summary-strip { display: none; }

  /* ── Entrada escalonada (solo tiras protagonistas, como v1) ─────────── */
  @keyframes kpi-rise {
    from { opacity: 0; transform: translateY(9px); }
    to   { opacity: 1; transform: none; }
  }

  @media (prefers-reduced-motion: no-preference) {
    :is(.overview-kpi-grid, .book-summary, .sales-summary,
        .report-kpi-strip, .mom-kpis) > * {
      animation: kpi-rise .44s var(--ease) both;
    }
    :is(.overview-kpi-grid, .book-summary, .sales-summary,
        .report-kpi-strip, .mom-kpis) > :nth-child(2) { animation-delay: .05s; }
    :is(.overview-kpi-grid, .book-summary, .sales-summary,
        .report-kpi-strip, .mom-kpis) > :nth-child(3) { animation-delay: .10s; }
    :is(.overview-kpi-grid, .book-summary, .sales-summary,
        .report-kpi-strip, .mom-kpis) > :nth-child(4) { animation-delay: .15s; }
    :is(.overview-kpi-grid, .book-summary, .sales-summary,
        .report-kpi-strip, .mom-kpis) > :nth-child(5) { animation-delay: .20s; }
    :is(.overview-kpi-grid, .book-summary, .sales-summary,
        .report-kpi-strip, .mom-kpis) > :nth-child(6) { animation-delay: .25s; }
    :is(.overview-kpi-grid, .book-summary, .sales-summary,
        .report-kpi-strip, .mom-kpis) > :nth-child(7) { animation-delay: .30s; }
  }

  /* ── Responsive (v1 tenía 1180/980/680/620: se consolida en 680;
        el auto-fit ya resuelve el reflujo de columnas solo) ─────────────── */
  @media (max-width: 680px) {
    :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
        .consumption-kpis, .inv-summary-strip, .recipe-technical-kpis,
        .supplier-detail-kpis, .annex-kpi-grid, .coffee-quote-kpis,
        .coffee-catalog-summary, .coffee-calendar-summary, .event-hub-steps,
        .run-list, .overview-kpi-grid) {
      gap: 10px;
    }

    :is(.book-summary, .sales-summary, .report-kpi-strip, .mom-kpis,
        .consumption-kpis, .recipe-technical-kpis, .supplier-detail-kpis,
        .annex-kpi-grid, .annex-match-summary, .coffee-catalog-summary,
        .coffee-calendar-summary, .event-hub-steps) > article,
    :is(.coffee-quote-kpis, .run-list, .inv-summary-strip) > div {
      min-height: 68px;
      padding: 12px 8px;
    }

    .overview-kpi { min-height: 118px; }
    .overview-kpi strong { font-size: var(--fs-h2); }
  }
}

/* líneas: 300 de presupuesto 300 */
/* ══ login-boot — arranque, login y wordmark compartido ═══════════════════
   Cubre: .app-loader (+.loading/.indeterminate/.is-hiding), .boot-logo/
     -line/-cost/-slot/-rule/-words/-word/-cl/-dot, .loader-progress-stage,
     .loader-progress, .loader-detail; .login-screen, .login-panel,
     .login-brand (+.lb-cost/.lb-dot/.lb-cl), .login-hint; .brandmark,
     .bm-cost/-slot/-rule/-words/-word/-cl/-dot — wordmark compartido con
     el riel (NO cubre .sidebar-brand: contenedor que posiciona y colorea,
     vive en el componente del riel — aquí solo currentColor).
   Fuente v1: styles.css ~L138-450 (boot + login), ~L1249-1298 (bm-*, sin
     su envoltorio .sidebar-brand). Ninguna capa posterior (v117/118/120/
     150+) vuelve a tocar esta familia: fusión trivial, una sola definición.
   Estados: .app-loader es role="status", no interactivo — sin hover/focus/
     active/disabled; .loading/.indeterminate son sus variantes de progreso
     (estadoJS), .is-hiding su salida; error no aplica (el watchdog de app.js
     solo lo oculta, el aviso vive en connection-state); vacío no aplica
     (contenido fijo). .login-panel: foco/hover/active/disabled ya resueltos
     por 02-base y boton.css; error es un cambio de TEXTO en .login-hint
     (app.js ~L2920/20962) + toast aparte, sin color propio (v1 tampoco lo
     traía). .brandmark/.bm-*: decorativo (aria-hidden), sin interacción;
     reduced-motion deja la primera palabra fija y oculta el resto.
   Contenidos límite: .loader-detail/.login-hint reciben texto JS de largo
     variable — envuelven, nunca truncan. Sin cifras ni datos ausentes: el
     componente no muestra datos, solo estado de arranque y credenciales.
   Cambios visibles: el lockup de boot pasa de tipografía fluida 34-60px
     (clamp) a --fs-title (29px, tope de escala); el cursivo que antes lo
     superaba (1.58em) iguala ese tope en vez de excederlo, y sus guardas
     max(Npx,.045em) se simplifican a un px plano al ya no ser fluidos. En
     el riel, brandmark sube de 1.05rem a --fs-h3 y su cursivo respira un
     escalón arriba (--fs-h2) para no perder el acento. El fondo del splash
     cambia el degradé hex propio de v1 por --nav/--nav-2 (misma tinta del
     riel): sigue siendo cortina de marca, ahora 100% tokens.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── Splash de arranque: pantalla completa, tinta de marca sobre tinta.
     Deliberadamente oscuro siempre — no es un tema, es la cortina de marca;
     no depende de --bg/--surface del documento. ───────────────────────── */
  .app-loader {
    --loader-progress: 0%;
    --loader-ink: var(--surface);
    --loader-ink-muted: color-mix(in srgb, var(--surface) 60%, transparent);
    --loader-track: color-mix(in srgb, var(--surface) 12%, var(--nav));
    position: fixed;
    inset: 0;
    z-index: var(--z-loader);
    display: grid;
    place-items: center;
    align-content: center;
    gap: 40px;
    padding: 28px;
    overflow: hidden;
    background: radial-gradient(130% 130% at 28% 18%, var(--nav-2) 0%, var(--nav) 70%);
    color: var(--loader-ink);
  }

  /* ── Lockup "cost ___ cl •": la palabra cursiva cae en el slot; el punto
     barre de izquierda a derecha revelando la línea al entrar. ─────────── */
  .boot-logo {
    position: relative;
    display: inline-flex;
    align-items: flex-end;
    padding-right: 16px;             /* aire para el punto, a la derecha de "cl" */
    font-family: var(--font-numeric);   /* Inter Tight 800 — nunca serif */
    font-weight: 800;
    font-size: var(--fs-title);
    line-height: 1;
    letter-spacing: -.012em;
    user-select: none;
  }

  .boot-line {
    display: inline-flex;
    align-items: flex-end;
    /* el punto revela esta cuña de izquierda a derecha al entrar */
    clip-path: inset(-300% 100% -300% 0);
    animation: boot-reveal 1.4s cubic-bezier(.6, .02, .16, 1) .12s forwards;
  }

  .boot-cost, .boot-cl { display: inline-block; }

  .boot-slot {
    position: relative;
    width: 136px;                    /* ancho constante del "____", cualquiera sea la palabra */
    height: 1em;
    margin: 0 4px;
  }

  .boot-rule {
    position: absolute;
    inset: auto 0 0 0;
    height: 2px;
    border-radius: 999px;
    background: var(--loader-ink);
  }

  .boot-words { position: absolute; inset: 0; }

  .boot-word {
    position: absolute;
    left: 50%;
    bottom: -4px;
    white-space: nowrap;
    font-family: "Sacramento", "Snell Roundhand", "Segoe Script", cursive;
    font-weight: 400;
    font-size: var(--fs-title);
    line-height: 1;
    color: var(--loader-ink);
    opacity: 0;
    transform: translate(-50%, -1.8em);
    animation: boot-cascade 27.5s linear infinite;
  }

  /* 11 palabras × 2.5s de reel = 27.5s (ver @keyframes boot-cascade) */
  .boot-word:nth-child(1)  { animation-delay: 1.7s;  }
  .boot-word:nth-child(2)  { animation-delay: 4.2s;  }
  .boot-word:nth-child(3)  { animation-delay: 6.7s;  }
  .boot-word:nth-child(4)  { animation-delay: 9.2s;  }
  .boot-word:nth-child(5)  { animation-delay: 11.7s; }
  .boot-word:nth-child(6)  { animation-delay: 14.2s; }
  .boot-word:nth-child(7)  { animation-delay: 16.7s; }
  .boot-word:nth-child(8)  { animation-delay: 19.2s; }
  .boot-word:nth-child(9)  { animation-delay: 21.7s; }
  .boot-word:nth-child(10) { animation-delay: 24.2s; }
  .boot-word:nth-child(11) { animation-delay: 26.7s; }

  .boot-dot {
    position: absolute;
    left: 0;
    bottom: 3px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--dot);
    box-shadow: 0 0 10px color-mix(in srgb, var(--dot) 60%, transparent),
                0 0 3px color-mix(in srgb, var(--dot) 90%, transparent);
    transform: translateX(-50%);
    animation: boot-dot-sweep 1.4s cubic-bezier(.6, .02, .16, 1) .12s forwards;
  }

  /* ── Detalle + barra de progreso ──────────────────────────────────────── */
  .loader-detail {
    min-height: 20px;
    margin: 0;
    text-align: center;
    color: var(--loader-ink-muted);
    font-size: var(--fs-small);
    font-weight: 650;
  }

  .loader-progress-stage {
    position: relative;
    z-index: 1;                      /* delante: las palabras que caen pasan detrás */
    width: min(360px, calc(100vw - 56px));
    height: 6px;
    display: grid;
    align-items: center;
  }

  .loader-progress {
    position: relative;
    width: 100%;
    height: 6px;
    overflow: hidden;
    border: 1px solid color-mix(in srgb, var(--surface) 12%, transparent);
    border-radius: 999px;
    background: var(--loader-track);
  }

  .loader-progress span {
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--loader-progress);
    min-width: 8px;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--dot), var(--amber-bright));
    box-shadow: 0 0 14px color-mix(in srgb, var(--dot) 45%, transparent);
    transition: width .24s var(--ease);
  }

  .app-loader.indeterminate .loader-progress span {
    width: 46%;
    animation: loader-progress-run 1.1s ease-in-out infinite;
  }

  .app-loader.loading .loader-progress span::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg,
      transparent, color-mix(in srgb, var(--surface) 50%, transparent), transparent);
    transform: translateX(-100%);
    animation: loader-shimmer 1.15s linear infinite;
  }

  .app-loader.is-hiding {
    pointer-events: none;
    animation: loader-fade-out .28s ease forwards;
  }

  /* Las dos animaciones de la barra: recorrido (indeterminate) y brillo (loading) */
  @keyframes loader-progress-run {
    0%   { left: -48%; width: 42%; }
    50%  { left: 30%;  width: 54%; }
    100% { left: 106%; width: 42%; }
  }
  @keyframes loader-shimmer { to { transform: translateX(100%); } }

  /* Intro (una sola vez): el punto rojo aparece y barre a la derecha,
     revelando "cost ____ cl" a su paso */
  @keyframes boot-dot-sweep {
    0%   { left: 3%;   opacity: 0; transform: translateX(-50%) scale(.35); }
    9%   { left: 3%;   opacity: 1; transform: translateX(-50%) scale(1); }
    100% { left: 100%; opacity: 1; transform: translateX(-50%) scale(1); }
  }
  @keyframes boot-reveal {
    0%, 9% { clip-path: inset(-300% 100% -300% 0); }
    100%   { clip-path: inset(-300% 0% -300% 0); }
  }

  /* Cascada vertical continua: cada palabra cruza la línea en 3 tiempos —
     entra tenue arriba, brilla al cruzar, se apaga cayendo detrás de la
     barra. 11 palabras × 2.5s = 27.5s de vuelta; ventana ≈ 3/11 ≈ 27.3%. */
  @keyframes boot-cascade {
    0%           { opacity: 0;   transform: translate(-50%, -1.8em); }
    4%           { opacity: .34; }
    13.64%       { opacity: 1;   transform: translate(-50%, 0); }
    23%          { opacity: .34; }
    27.27%       { opacity: 0;   transform: translate(-50%, 1.8em); }
    27.28%, 100% { opacity: 0;   transform: translate(-50%, -1.8em); }
  }
  @keyframes loader-fade-out { to { opacity: 0; visibility: hidden; } }

  @media (max-width: 680px) {
    .app-loader { gap: 24px; padding: 20px; }
    .loader-progress-stage { width: min(300px, calc(100vw - 48px)); }
  }

  /* El interruptor global de 01-reset ya frena animation-duration; acá se
     fija el fotograma de reposo correcto (sin esto, opacity:0 quedaría fija) */
  @media (prefers-reduced-motion: reduce) {
    .boot-line, .boot-dot, .boot-word { animation: none; }
    .boot-line { clip-path: inset(-300% 0% -300% 0); }
    .boot-dot { left: 100%; opacity: 1; transform: translateX(-50%); }
    .boot-word:nth-child(1) { opacity: 1; transform: translate(-50%, 0); }
    .app-loader.indeterminate .loader-progress span { animation: none; }
  }

  /* ── Login ─────────────────────────────────────────────────────────────
     Inputs/labels/botón: 02-base + boton.css ya los resuelven — aquí solo
     la tarjeta y el wordmark. */
  .login-screen {
    min-height: 100vh;
    display: grid;
    place-items: center;
    padding: 24px;
    background: var(--bg);
  }

  .login-panel {
    width: min(380px, 100%);
    display: grid;
    gap: 14px;
    padding: 28px;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-lg);
  }

  /* Wordmark cost•cl: mismo lockup que el sitio, versión estática (v1 h1
     28px → --fs-h2, mismo tamaño que las cabeceras de modal) */
  .login-brand {
    display: inline-flex;
    align-items: baseline;
    gap: 2px;
    margin-bottom: 4px;
    font-family: var(--font-numeric);
    font-size: var(--fs-h2);
    font-weight: 800;
    letter-spacing: -.03em;
  }

  .login-brand .lb-cost { color: var(--ink); }
  .login-brand .lb-dot  { color: var(--dot); }
  .login-brand .lb-cl   { color: var(--accent); }

  .login-hint {
    margin: 0;
    color: var(--muted-2);
    font-size: var(--fs-small);
  }

  /* ── Wordmark del riel (currentColor: el color lo fija .sidebar-brand,
     no acá). Mismo lockup que boot, en miniatura y en bucle silencioso. ── */
  .brandmark {
    display: inline-flex;
    align-items: flex-end;
    font-size: var(--fs-h3);
    line-height: 1;
    color: inherit;
    white-space: nowrap;
    user-select: none;
  }

  .bm-cost, .bm-cl {
    font-family: var(--font-numeric);
    font-weight: 800;
    letter-spacing: -.02em;
  }

  .bm-slot { position: relative; flex: none; width: 50px; height: 1em; margin: 0 4px; }

  .bm-rule {
    position: absolute;
    inset: auto 0 2px 0;
    height: 1.5px;
    border-radius: 999px;
    background: currentColor;
    opacity: .9;
  }

  .bm-words { position: absolute; inset: 0; }

  .bm-word {
    position: absolute;
    left: 50%;
    bottom: -6px;
    transform: translateX(-50%);
    white-space: nowrap;
    font-family: "Sacramento", "Snell Roundhand", "Segoe Script", cursive;
    font-weight: 400;
    font-size: var(--fs-h2);        /* un escalón sobre el lockup: el acento cursivo */
    line-height: 1;
    opacity: 0;
    animation: bm-rotate 15s linear infinite;
  }

  .bm-word:nth-child(1) { animation-delay: 0s;  }
  .bm-word:nth-child(2) { animation-delay: 3s;  }
  .bm-word:nth-child(3) { animation-delay: 6s;  }
  .bm-word:nth-child(4) { animation-delay: 9s;  }
  .bm-word:nth-child(5) { animation-delay: 12s; }

  .bm-dot {
    flex: none;
    align-self: flex-end;
    width: 4px;
    height: 4px;
    margin: 0 0 2px 6px;
    border-radius: 50%;
    background: var(--dot);
  }

  @keyframes bm-rotate {
    0%   { opacity: 0; transform: translate(-50%, 2px); }
    2.5% { opacity: 1; transform: translate(-50%, 0); }
    18%  { opacity: 1; transform: translate(-50%, 0); }
    20%  { opacity: 0; transform: translate(-50%, -2px); }
    100% { opacity: 0; transform: translate(-50%, -2px); }
  }

  @media (prefers-reduced-motion: reduce) {
    .bm-word { animation: none; opacity: 0; transform: translateX(-50%); }
    .bm-word:first-child { opacity: 1; }
  }
}

/* líneas: ~374 de presupuesto 300 (+74: tres familias reales con coreografía
   propia — reel de 11 palabras en boot, giro de 5 en el riel, dos motores
   de barra de progreso, dos bloques de reduced-motion — nada queda sin
   cubrir para compactar más sin perder legibilidad) */
/* ══ modal ═════════════════════════════════════════════════════════════════
   Cubre: .modal-backdrop, .modal-backdrop--top, .modal-panel, .modal-grid,
     .modal-actions, botón cerrar (icon-button en el head), .app-dialog,
     .app-dialog-backdrop, .app-dialog-title, .app-dialog-message,
     .app-dialog-field, y las variantes de tamaño del contrato:
     .order-confirm-modal, .promotion-modal, .settings-modal,
     .inventory-import-modal, .calendar-event-modal, .order-supplier-modal,
     .recipe-technical-modal, .settings-modal--wide, .report-detail-modal,
     .coffee-catalog-modal, .order-import-modal, .recipe-edit-modal,
     .benchmark-detail-modal, .catalog-export-modal, .settings-modal-toolbar,
     .settings-modal-footer, .hr-modal, .hr-modal-wide, .hr-modal-head,
     .hr-modal-backdrop
   Fuente v1: styles.css ~L5964-6030 (chrome + app-dialog), ~L5445, ~L5503,
     ~L6808, ~L7273, ~L8075, ~L11010, ~L14654-14665, ~L15786, ~L18350-18378,
     ~L18566, ~L10516 (responsive), ~L14211-14258 (animación);
     hr.css ~L152, ~L273-277, ~L591-592 (fusionadas)
   Estados: reposo · entrada animada · scroll interno. Hover/focus/disabled
     viven en los controles internos (base y botones); "vacío" no aplica:
     un modal sin contenido nunca se abre.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── Backdrop: tinta de marca velada + blur (canon §4) ─────────────────
     [hidden] lo apaga el !important del reset; no se repite aquí. */
  .modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: grid;
    place-items: center;
    padding: 20px;
    background: color-mix(in srgb, var(--ink) 42%, transparent);
    -webkit-backdrop-filter: blur(5px);
    backdrop-filter: blur(5px);
  }

  /* Modal que se abre SOBRE otro modal (catálogos de eventos) */
  .modal-backdrop--top { z-index: var(--z-modal-top); }

  /* ── Panel ─────────────────────────────────────────────────────────────
     Scroll interno unificado: v1 mezclaba 88vh / 92vh / calc(100vh-36|42px);
     aquí una sola política. 22px de padding = mandato del chrome canónico
     (excepción documentada a la escala de espaciado). */
  .modal-panel {
    width: min(720px, 100%);
    max-height: 94vh;
    overflow: auto;
    padding: 22px;
    background: var(--surface);
    border: 1px solid var(--line-2);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-lg);
  }

  /* ── Entrada: fade del velo + pop del panel ────────────────────────────
     prefers-reduced-motion la neutraliza el interruptor global del reset.
     v1 tenía además calendarModalIn propio: se unifica a este único pop. */
  .modal-backdrop:not([hidden]) { animation: modal-velo-in .16s var(--ease); }
  .modal-backdrop:not([hidden]) .modal-panel { animation: modal-pop-in .22s var(--ease-pop); }

  @keyframes modal-velo-in { from { opacity: 0; } }
  @keyframes modal-pop-in {
    from { opacity: 0; transform: translateY(10px) scale(.985); }
  }

  /* ── Interior genérico ─────────────────────────────────────────────────
     .modal-grid es la retícula de formulario de 3 columnas; las retículas
     semánticas por vista (calendar-event-grid, etc.) viven en sus vistas. */
  .modal-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
  }

  .modal-actions {
    margin-top: 16px;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
  }

  /* Botón cerrar: SIEMPRE un .icon-button con "×" en el head del modal
     (section-head o hr-modal-head, hijos estructurales del panel).
     v1 lo dejaba al tamaño de texto (13px): aquí el glifo respira. */
  .modal-panel > .section-head .icon-button,
  .hr-modal-head .icon-button {
    flex: none;
    font-size: var(--fs-h3);
    font-weight: 400;
    line-height: 1;
  }

  /* ── Variantes de tamaño (orden ascendente: en combinaciones tipo
     .settings-modal.catalog-export-modal gana la declarada después).
     min(Npx, 100%) resuelve contra el padding del backdrop (20px por lado),
     que reemplaza los calc(100vw - 28|36px) ad-hoc de v1.
     #inventoryAddItemModal usa el ancho base, sin variante;
     #orderSentConfirmModal ya lleva .order-confirm-modal (520, abajo);
     #consumptionTrendModal reutiliza .benchmark-detail-modal. ─────────── */
  .order-confirm-modal { width: min(520px, 100%); }
  .order-confirm-modal p { color: var(--ink-2); font-size: var(--fs-body); line-height: 1.55; }

  .promotion-modal {
    width: min(680px, 100%);
    display: flex;
    flex-direction: column;
    gap: 14px;
  }

  .settings-modal { width: min(720px, 100%); }   /* igual a la base: explícito */
  .inventory-import-modal { width: min(760px, 100%); }
  .calendar-event-modal { width: min(780px, 100%); }
  .order-supplier-modal { width: min(980px, 100%); }
  .recipe-technical-modal { width: min(1100px, 100%); }
  .settings-modal--wide { width: min(1120px, 100%); }

  /* Detalle de reporte: head fijo + cuerpo que absorbe la altura */
  .report-detail-modal {
    width: min(1120px, 100%);
    min-height: 500px;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    gap: 12px;
  }
  .report-detail-table { min-height: 0; height: 70vh; }

  .coffee-catalog-modal,
  .order-import-modal { width: min(1180px, 100%); }

  .recipe-edit-modal,
  .benchmark-detail-modal { width: min(1240px, 100%); }

  /* Espacio de trabajo del catálogo: altura fija para que su grilla
     interna (columna izquierda + lienzo) reparta con flex */
  .catalog-export-modal {
    width: min(1520px, 100%);
    height: 92vh;
    display: flex;
    flex-direction: column;
  }

  /* ── Modales iOS de Ajustes (el backdrop vive DENTRO del markup de la
     vista, pero position:fixed lo saca igual a pantalla completa) ──────── */
  .settings-modal-toolbar {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin: 14px 0;
  }

  .settings-modal-toolbar .section-note { max-width: 620px; }

  /* Compone sobre .button-row: solo alinea y separa */
  .settings-modal-footer {
    justify-content: flex-end;
    margin-top: 16px;
  }

  /* ── Diálogo propio (reemplaza a window.confirm/prompt) ────────────────
     Markup JS: .modal-backdrop.app-dialog-backdrop > .modal-panel.app-dialog */
  .app-dialog-backdrop { z-index: var(--z-dialog); }

  .app-dialog { width: min(480px, 100%); }

  .app-dialog-title {
    margin: 0 0 8px;
    font-family: var(--font-display);
    font-size: var(--fs-h2);
    font-weight: 400;
    letter-spacing: -.015em;
  }

  .app-dialog-message {
    color: var(--muted);
    font-size: var(--fs-body);
    line-height: 1.55;
  }

  .app-dialog-field { margin-top: 14px; }

  /* ── Variante RRHH: panel sin padding con head propio; el scroll vive en
     el BACKDROP (flex-start + overflow) para que modales muy altos partan
     anclados arriba. v1 omitía el centrado horizontal al pasar a flex;
     aquí se corrige con justify-content. ────────────────────────────────── */
  .modal-backdrop.hr-modal-backdrop {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    overflow-y: auto;
    padding: 28px 16px;
  }

  .hr-modal {
    width: min(760px, 100%);
    max-height: none;      /* el scroll ya lo da el backdrop */
    overflow: visible;
    padding: 0;            /* head y cuerpo (hr-form) traen su propio padding */
  }

  .hr-modal-wide { width: min(1080px, 100%); }

  .hr-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 20px 24px;
    border-bottom: 1px solid var(--line);
  }

  /* Título canon de modal (v1 usaba Inter Tight 750/25px: cambio visible) */
  .hr-modal-head h2 {
    margin: 2px 0 0;
    font-family: var(--font-display);
    font-size: var(--fs-h2);
    font-weight: 400;
    letter-spacing: -.015em;
  }

  /* Hijo estructural: alertas sueltas dentro del panel sin padding */
  .hr-modal > .hr-alert { margin-left: 24px; margin-right: 24px; }

  /* ── Responsive (v1: 980px y 720px ad-hoc → 900/680 canon) ───────────── */
  @media (max-width: 900px) {
    .modal-grid { grid-template-columns: 1fr; }
  }

  @media (max-width: 680px) {
    .modal-backdrop { padding: 12px; }

    /* RRHH pasa a hoja completa */
    .modal-backdrop.hr-modal-backdrop { padding: 0; }
    .hr-modal,
    .hr-modal-wide {
      width: 100vw;
      min-height: 100vh;
      border-radius: 0;
    }
  }
}

/* líneas: ~245 de presupuesto 400 */
/* ══ navegación superior ═════════════════════════════════════════════════════
   Cubre: .topbar (+ .tb-marca, .tb-equis, .tb-cats, .tb-cat, .tb-der,
          .tb-progreso), .tb-panel (+ .abierto), .tb-velo, y el re-encuadre de
          .brand / .nav-search / .nav-list / .connection-state / .user-state /
          .sidebar-brand cuando viven DENTRO de la franja.
   Fuente v1: no existe — modo nuevo, opcional desde Ajustes. El riel lateral
          (03-layout.css) sigue siendo el modo por defecto y no se toca.
   Contrato: los MISMOS nodos del riel se mudan a la franja (igual que el
          período al header y los filtros al cajón). No hay menú duplicado: si
          se añade una vista al riel, aparece arriba sin tocar nada.
   Estados: cerrado · categoría abierta · vista activa · conectando/procesos.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── El lienzo cambia de eje ────────────────────────────────────────────
     Una columna y dos filas: franja arriba, trabajo abajo. --nav-alto deja de
     ser 0 para que los topes `calc(100dvh - var(--nav-alto) - N)` de las
     vistas descuenten la franja sin repetir un solo número. */
  /* En :root y no solo en el shell porque el panel y el velo son `fixed` a
     nivel de body: si --nav-alto no llega ahí, el panel abre en top:0 y tapa
     las propias categorías que acabas de pulsar. */
  :root[data-nav="superior"] { --nav-alto: 52px; }

  .app-shell[data-nav="superior"] {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }
  .app-shell[data-nav="superior"] > .sidebar { display: none; }
  .app-shell:not([data-nav="superior"]) > .topbar { display: none; }

  /* ── La franja ──────────────────────────────────────────────────────────
     Alto clavado en 52px: es lo que se le resta a la tabla, y si respira con
     el contenido deja de cuadrar con --nav-alto. */
  .topbar {
    /* Pegajosa: si la franja se fuera con el scroll, la navegación
       desaparecería justo cuando más se necesita. Y la barra de vista se pega
       debajo de ella (top: var(--nav-alto)), así que las dos viajan juntas. */
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    display: flex;
    align-items: center;
    gap: 6px;
    height: var(--nav-alto);
    padding: 0 14px;
    background: var(--nav);
  }

  /* ── Firma conjunta: la casa × la plataforma ────────────────────────────
     Pesos distintos a propósito. Al mismo tamaño se lee como una fusión —una
     empresa llamada "Bruma cost.cl"— y no como "esto corre sobre". */
  .topbar > .brand {
    flex: none;
    display: flex; align-items: center;
    min-width: 0; padding: 0; margin: 0;
    border: 0; background: none;
  }
  .topbar > .brand .brand-mark { width: auto; height: 30px; padding: 0; }
  .topbar > .brand .brand-mark img { width: auto; height: 100%; max-height: 30px; }
  .topbar > .brand.is-wide-logo .brand-mark,
  .topbar > .brand.is-wide-logo .brand-mark img { width: auto; max-height: 30px; height: 30px; }

  .tb-equis {
    flex: none;
    margin: 0 10px;
    color: var(--rail-muted);
    font-size: 11px; line-height: 1;
  }

  .topbar > .sidebar-brand {
    flex: none;
    padding: 0 15px 0 0; margin: 0 4px 0 0;
    border-right: 1px solid var(--rail-line-2);
  }
  .topbar > .sidebar-brand .brandmark { font-size: var(--fs-body-lg); color: var(--rail-soft); }

  /* ── Buscador, antes de Inicio ──────────────────────────────────────────
     Puesto ahí deja de ser un accesorio de esquina: se lee como la primera
     forma de moverse —escribir el destino— en vez de desplegar el menú.
     Es el primero que cede cuando falta sitio (tiene atajo de teclado). */
  .topbar > .nav-search {
    flex: none;
    width: 196px; margin: 0 8px 0 0; padding: 0;
  }
  .topbar > .nav-search input {
    height: 32px; min-height: 32px;
    border-radius: 999px;
    border-color: transparent;
    background: var(--rail-veil-2);
    color: var(--rail-ink);
  }
  .topbar > .nav-search input::placeholder { color: var(--rail-muted); }
  .topbar > .nav-search input:focus { background: var(--rail-veil-3); border-color: transparent; }

  /* ── Las categorías ─────────────────────────────────────────────────────
     No ceden ancho: son la navegación. Todo lo que se encoge está a su
     derecha. Con min-width:0 los botones se desbordaban por debajo del
     buscador y "Gestión" quedaba escrito encima de "Buscar vista". */
  .tb-cats { flex: none; display: flex; align-items: center; gap: 2px; }

  .tb-cat {
    display: inline-flex; align-items: center; gap: 6px;
    height: 34px; padding: 0 12px;
    border: 0; border-radius: var(--r-sm);
    background: none;
    color: var(--rail-soft);
    font-size: var(--fs-body); font-weight: 650;
    white-space: nowrap;
    transition: background var(--t), color var(--t);
  }
  .tb-cat:hover { background: var(--rail-veil-2); color: var(--rail-ink); }
  .tb-cat[aria-expanded="true"] { background: var(--rail-veil-3); color: var(--rail-ink); }
  .tb-cat.tiene-activa { color: var(--rail-ink); }

  .tb-flecha {
    width: 7px; height: 7px; margin-left: 1px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(45deg) translate(-1px, -1px);
    opacity: .55;
    transition: transform var(--t), opacity var(--t);
  }
  .tb-cat[aria-expanded="true"] .tb-flecha { transform: rotate(-135deg) translate(-2px, -2px); opacity: 1; }

  /* Ítem suelto (Taller): sin desplegable, con punto en vez de flecha. */
  .tb-cat--suelto { color: var(--rail-muted); }
  .tb-cat--suelto::before {
    content: ""; flex: none;
    width: 5px; height: 5px; border-radius: 50%;
    background: currentColor;
  }

  /* ── Zona derecha: estado y usuario ─────────────────────────────────────
     Lo que vivía en el pie del riel. La campana y el recargar NO viven aquí:
     siguen en la barra de la vista, que es su sitio. */
  .tb-der { margin-left: auto; flex: none; display: flex; align-items: center; gap: 8px; }

  /* Conectada y sin trabajo pendiente es la situación normal: no hace falta
     decirla con palabras. Un punto verde basta, y los ~110px que ahorra los
     necesitan las categorías. Cuando algo corre, se abre y lo cuenta. */
  .topbar .connection-state {
    display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    height: 30px; padding: 0; width: 30px;
    border-radius: 999px;
    background: var(--rail-veil-2);
    color: var(--rail-soft);
    font-size: var(--fs-small); font-weight: 650;
    cursor: pointer;
  }
  .topbar .connection-state:hover { background: var(--rail-veil-3); }
  .topbar .connection-row { display: contents; }
  .topbar .connection-progress { display: none; }
  .topbar #connectionLabel { display: none; }
  .topbar .connection-percent { display: none; color: var(--rail-muted); font-variant-numeric: tabular-nums; }

  /* Con procesos en marcha la pastilla se abre y muestra la cuenta. */
  .topbar .connection-state[data-ocupado="1"] { width: auto; padding: 0 12px; }
  .topbar .connection-state[data-ocupado="1"] #connectionLabel,
  .topbar .connection-state[data-ocupado="1"] .connection-percent { display: inline; }

  .topbar .connection-task-panel {
    position: absolute; top: 46px; right: 14px;
    width: 268px; padding: 12px 14px 14px;
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-lg);
    z-index: var(--z-popover);
  }

  .topbar .user-state {
    display: flex; align-items: center; gap: 8px;
    padding: 0; border: 0; background: none;
  }
  .topbar .user-identity {
    height: 30px; padding: 0 12px;
    border: 0; border-radius: 999px;
    background: var(--rail-veil-2);
    color: var(--rail-ink);
  }
  .topbar .user-identity svg { color: var(--rail-muted); }
  .topbar .sidebar-foot-actions { gap: 4px; }
  .topbar .sidebar-settings-button,
  .topbar #logoutBtn {
    height: 30px; min-height: 30px;
    border-color: transparent;
    background: var(--rail-veil-2);
    color: var(--rail-soft);
    box-shadow: none;
  }
  .topbar .sidebar-settings-button:hover,
  .topbar #logoutBtn:hover { background: var(--rail-veil-3); color: var(--rail-ink); }

  /* ── El progreso, como hilo al pie de la franja ─────────────────────────
     En el riel era una barra de 190px en una esquina. A todo el ancho se ve
     desde cualquier punto de la pantalla y no cuesta ni un píxel de alto:
     vive sobre el borde inferior. */
  /* En el riel la carga es una barra con rótulo y porcentaje; aquí se
     convierte en un hilo al pie de la franja, y con 2px de verde apagado
     sobre fondo oscuro no se veía: había información que en el riel se leía y
     aquí se perdía. 3px, verde vivo y un halo que lo despega del fondo.
     Al llegar a 100 no se desvanece de inmediato: espera medio segundo, para
     que el final del proceso se vea en vez de intuirse. */
  .tb-progreso {
    position: absolute; left: 0; right: 0; bottom: 0;
    height: 3px;
    background: var(--rail-veil-2);
    overflow: hidden;
  }
  .tb-progreso > i {
    display: block; height: 100%; width: 0;
    background: linear-gradient(90deg, var(--green-vivo), var(--cyan));
    box-shadow: 0 0 10px color-mix(in srgb, var(--green-vivo) 85%, transparent);
    transition: width var(--t-lg), opacity var(--t-lg);
  }
  .tb-progreso[data-lleno="1"] > i { opacity: 0; transition-delay: 0s, .5s; }

  /* ── El desplegable, anclado bajo su categoría ──────────────────────────
     Cae del ancho que necesita —no del ancho de la pantalla— y flota sobre el
     contenido sin moverlo. Esquinas inferiores redondas: la franja tiene el
     filo recto arriba y la caída lo suaviza abajo. */
  .tb-panel {
    position: fixed;
    top: var(--nav-alto);
    z-index: var(--z-modal-top);
    /* Al ancho de su contenido, no al de la pantalla: Libros pide una columna
       y Finanzas cinco. Con un ancho fijo, o Libros quedaba con cuatro
       columnas vacías o a Finanzas se le salía "Activos" por el filo. */
    width: max-content;
    max-width: min(1024px, calc(100vw - 28px));
    padding: 18px 20px 20px;
    border-radius: 0 0 20px 20px;
    background: var(--nav);
    box-shadow: 0 26px 50px -26px color-mix(in srgb, var(--ink) 55%, transparent);
    animation: tb-caer .22s var(--ease-pop);
  }
  .tb-panel[hidden] { display: none; }
  @keyframes tb-caer { from { opacity: 0; transform: translateY(-10px); } }

  /* ── Resultados del buscador ────────────────────────────────────────────
     En el riel, buscar apaga las vistas que no calzan y las que quedan se ven
     ahí mismo. Arriba no hay lista a la vista —los destinos viven dentro de
     los desplegables— así que el filtro no enseñaba nada: se escribía y no
     pasaba nada. Los resultados caen en el mismo panel, bajo el buscador. */
  .tb-resultados { display: grid; gap: 2px; min-width: 232px; }
  .tb-resultado {
    display: flex; align-items: center; gap: 10px;
    padding: 9px 12px;
    border: 0; border-radius: var(--r-sm);
    background: none;
    color: var(--rail-soft);
    font: inherit; font-size: var(--fs-body); font-weight: 650;
    text-align: left;
    cursor: pointer;
  }
  .tb-resultado:hover,
  .tb-resultado:focus-visible { background: var(--rail-veil-2); color: var(--rail-ink); }
  .tb-resultado small { margin-left: auto; color: var(--rail-muted); font-size: var(--fs-caption); font-weight: 650; }
  .tb-resultados-nada { padding: 10px 12px; color: var(--rail-muted); font-size: var(--fs-small); }

  .tb-velo {
    position: fixed; inset: var(--nav-alto) 0 0 0;
    z-index: var(--z-modal);
    background: color-mix(in srgb, var(--ink) 18%, transparent);
    animation: tb-velo-in .16s var(--ease);
  }
  .tb-velo[hidden] { display: none; }
  @keyframes tb-velo-in { from { opacity: 0; } }

  /* ── Umbrales, medidos ────────────────────────────────────────────────
     La franja completa necesita 1.289px; con el buscador en icono, 1.125; y
     sin el wordmark, 958. Los saltos van justo encima de cada número, no en
     los redondos de siempre: puestos más abajo la barra empujaba la página a
     lo ancho antes de que ninguna regla llegara a aliviarla. */

  /* 1 · El buscador cede primero: tiene atajo de teclado. */
  @media (max-width: 1300px) {
    .topbar > .nav-search { width: 32px; }
    .topbar > .nav-search input { padding-inline: 0; text-indent: -9999px; }
  }

  /* 2 · Después la marca del producto, que acompaña pero no navega. */
  @media (max-width: 1140px) {
    .topbar > .sidebar-brand { display: none; }
    .tb-equis { display: none; }
  }

  /* 3 · Y por último las categorías absorben el apretón scrolleando dentro de
     su banda, en vez de empujar la página entera a lo ancho. */
  @media (max-width: 980px) {
    .tb-cats {
      flex: 1 1 auto;
      min-width: 0;
      overflow-x: auto;
      scrollbar-width: none;
    }
    .tb-cats::-webkit-scrollbar { display: none; }
  }
}

/* ── Re-encuadre del menú DENTRO del panel ───────────────────────────────────
   En @layer utilidades y no en componentes: 03-layout define la geometría del
   .nav-list para el riel (columna, grupos plegables, alto animado) y esa capa
   gana por orden. Dentro del panel manda el panel. */
@layer utilidades {

  /* El menú entero deja de verse: sus grupos salen de uno en uno al panel. */
  .app-shell[data-nav="superior"] .sidebar .nav-list { display: none; }

  .tb-panel .nav-group-body {
    display: block;
    max-height: none;
    overflow: visible;
    opacity: 1;
    transform: none;
    /* Una columna por sección. El número lo pone el JS contando los rótulos:
       Libros tiene 1, Finanzas tiene 5. Con un número fijo, Libros repartía
       dos destinos entre cinco columnas vacías. */
    column-count: var(--tb-cols, 1);
    column-width: 176px;
    column-gap: 24px;
  }
  .tb-panel .nav-group[data-collapsed="true"] .nav-group-body { display: block; }

  .tb-panel .nav-item {
    display: flex; align-items: center; gap: 8px;
    width: 100%; min-height: 34px; padding: 7px 10px;
    border-radius: var(--r-sm);
    color: var(--rail-soft);
    font-size: var(--fs-body); font-weight: 550;
    white-space: nowrap;
    break-inside: avoid;
  }
  .tb-panel .nav-item:hover { background: var(--rail-veil-3); color: var(--rail-ink); }
  .tb-panel .nav-item svg { width: 14px; height: 14px; opacity: .6; }
  .tb-panel .nav-item.active { background: var(--surface); color: var(--ink); }
  .tb-panel .nav-item.active svg { opacity: 1; color: var(--accent); }

  /* Cada rótulo abre columna: es lo que da al panel su forma de secciones. */
  .tb-panel .nav-subsection-label {
    display: block;
    margin: 0 0 6px; padding-left: 10px;
    color: var(--rail-muted);
    font-size: var(--fs-caption); font-weight: 700;
    letter-spacing: .14em; text-transform: uppercase;
    break-before: column;
    break-after: avoid;
  }
  /* El primero no rompe: ya está al principio de la primera columna. */
  .tb-panel .nav-group-body > .nav-subsection-label:first-child { break-before: auto; }

  .tb-panel .nav-group-toggle { display: none; }   /* el disparador vive en la franja */
}

/* líneas: 328 de presupuesto 300 */
/* ══ page-header ════════════════════════════════════════════════════════════
   Cubre: .page-header, .ph-top, .ph-toolbox (la barra), .ph-title-group,
          .ph-eyebrow (oculto), .ph-title, .ph-stats, .ph-stat (+ --green/
          --amber/--red), .ph-stat-value, .ph-stat-label, .ph-stat-sub
          (oculto), .ph-period (+ --vacio, .ph-period-nada, .ph-period-ayuda),
          .header-period-nav, .financial-close-month-picker, .top-actions,
          #refreshBtn (.is-busy/.is-done). La campanita completa
          (.notif-bell/.notif-bell-btn/.notif-badge/.notif-panel) vive
          ÍNTEGRA en feedback.css — aquí solo se posiciona en .top-actions.
   Fuente v1: styles.css ~L1330-1512, ~L6033-6055, ~L9894-9916, ~L13614-13697,
          ~L14051-14067, ~L14249-14254, ~L18023-18093, ~L18100-18210 (fusión)
   Estados: completo · .ph-stat no es interactivo (sin hover/focus/active);
          las celdas de la barra NO colapsan cuando están vacías: el hueco
          fijo es lo que mantiene la barra igual en todas las vistas.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── Marco full-bleed: los márgenes negativos calzan con el padding del
     .workspace (03-layout): 24/28 en escritorio, 20 bajo 900px ── */
  /* Pegajoso y transparente. Lo primero, para que el contenido pase POR
     DEBAJO de la cápsula al scrollear —sin eso el vidrio no tiene nada que
     refractar y se ve como un blanco translúcido cualquiera—. Lo segundo,
     porque un fondo blanco propio taparía el lienzo y mataría el efecto. */
  .page-header {
    position: sticky;
    /* 14 y no var(--nav-alto): el lienzo es contenedor de scroll (03-layout),
       así que este `top` se mide desde SU borde —que ya empieza bajo la
       franja— y poner ahí el alto del nav lo contaba dos veces: 90px de hueco
       y la cápsula montada sobre la tarjeta.
       Medido: el hueco sobre la cápsula es `padding-top del lienzo + top`, y
       el aire que conserva al quedar pegada es `top` a secas. Con el padding
       del lienzo en 0, los dos valen 14: la cápsula guarda la misma distancia
       bajo el nav esté quieta o pegada. */
    top: 14px;
    z-index: var(--z-sticky);
    /* Sin margen negativo arriba: el padding del lienzo ES el aire sobre la
       cápsula, y así queda a la misma distancia del borde por los cuatro
       lados. Los -28 laterales se mantienen para que la cápsula alinee con el
       contenido de la vista (los recupera el padding de .ph-top). */
    margin: 0 -28px 24px;
    background: none;
    border-bottom: 0;
  }

  /* (Antes Agenda y Trabajo diario escondían el header para ir a pantalla
     completa. Con la barra persistente eso ya no aplica: es el ancla común
     de la app —notificaciones, recargar, período— y debe estar en TODAS
     las vistas.) */

  /* ── El toolbox ES el header: ocupa el ancho completo ── */
  .ph-top {
    display: block;
    overflow: visible;
    padding: 0 28px 14px;
  }

  /* ══ TOOLBOX ═══════════════════════════════════════════════════════════
     Una sola barra a todo el ancho, idéntica en las 29 vistas que llevan
     header (Agenda y Trabajo diario van a pantalla completa, sin él), y
     siempre en el mismo orden: título · acciones · período · indicadores.
     Mide 54px pase lo que pase con el contenido. */
  .ph-toolbox {
    /* Cada compartimento cae siempre en la misma coordenada. El único que
       varía por dentro es el de indicadores, y lo hace scrolleando, no
       ensanchándose. */
    display: grid;
    grid-template-columns:
      minmax(0, 248px)      /* título */
      auto                  /* notificaciones + recargar */
      minmax(248px, auto)   /* período */
      auto                  /* filtros, junto al período */
      minmax(0, 1fr);       /* indicadores → scroll */
    /* El período pide 248 —lo que mide "enero de 2026" con sus flechas— pero
       puede crecer: Trabajo diario mete un <input type="date"> nativo con dos
       flechas y "Hoy", y clavado en 248 el navegador le recortaba el día por
       la izquierda. Lo que crezca se lo descuenta la celda de indicadores,
       que es la única elástica.
       Filtros iba al extremo derecho, al otro lado del carrusel de KPI: para
       pulsarlo había que cruzar la barra entera. Ahora va pegado al período,
       antes de los indicadores. */
    align-items: stretch;

    /* ── Cápsula de vidrio ──────────────────────────────────────────────
       Cuatro capas, y las cuatro hacen falta. Con desenfoque y blanco a
       secas sale plástico lechoso; lo que da la sensación de cristal es el
       CANTO: una línea clara arriba —la luz que entra por el borde— y una
       oscura abajo —el grosor proyectando sombra sobre lo que tapa—. Y la
       saturación, que hace que el color de abajo se vea vivo a través en
       vez de lavado. */
    position: relative;
    border-radius: 999px;
    padding: 0 8px;
    background: color-mix(in srgb, var(--surface) 62%, transparent);
    -webkit-backdrop-filter: blur(22px) saturate(180%);
    backdrop-filter: blur(22px) saturate(180%);
    border: 1px solid color-mix(in srgb, var(--surface) 55%, transparent);
    box-shadow:
      inset 0 1px 0 color-mix(in srgb, var(--surface) 85%, transparent),
      inset 0 -1px 0 color-mix(in srgb, var(--ink) 6%, transparent),
      0 10px 30px -14px color-mix(in srgb, var(--ink) 35%, transparent);
    isolation: isolate;   /* el fuego, si se enciende, no se escapa a la vista */
  }

  /* Los extremos redondos necesitan aire: pegado al filo, el texto se monta
     sobre la curva. */
  .ph-toolbox > .ph-title-group { padding-left: 22px; }
  .ph-toolbox > .filtros-btn {
    padding-right: 22px;
    border-radius: 0 999px 999px 0;
    /* Sin fondo propio: el botón trae el suyo blanco y dentro de la cápsula
       abría un parche opaco justo en el filo redondo. Aquí el fondo es el
       vidrio. */
    background: none;
  }
  .ph-toolbox > .filtros-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--surface) 45%, transparent); }
  .ph-toolbox > .filtros-btn:disabled { background: color-mix(in srgb, var(--ink) 4%, transparent); }

  /* Título dentro de la barra: sin eyebrow y en una línea. */
  .ph-title-group {
    min-width: 0;
    display: flex; align-items: center;
    padding: 0 16px;
    overflow: hidden;
  }
  .ph-eyebrow { display: none; }   /* la vista ya la marca el sidebar */
  .ph-title {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--fs-h2); font-weight: 400;
    letter-spacing: -.02em; line-height: 1.2;
    color: var(--brand);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }

  /* Los compartimentos se separan con una línea, no con aire: la barra se
     lee como un solo objeto dividido, no como cuatro piezas sueltas. */
  .ph-toolbox > * + * { border-left: 1px solid color-mix(in srgb, var(--surface) 45%, transparent); }

  /* 1 · Acciones: celda de ancho propio, siempre en el mismo sitio. */
  .ph-toolbox > .top-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 6px 10px;
  }

  /* 2 · Período: celda de ancho fijo. Nunca colapsa — si la vista no filtra
     por fechas, el hueco muestra el marcador apagado. */
  .ph-toolbox > .ph-period {
    min-width: 0;
    align-items: center;
    padding: 6px 12px;
    overflow: hidden;   /* nada se derrama sobre los indicadores vecinos */
  }

  /* El navegador de mes traía su propio rótulo "PERIODO" y con él medía
     279px contra una celda de 232: se salía encima de los indicadores. El
     rótulo sobra —la celda entera ES el período— y sin él cabe holgado. */
  .ph-toolbox .financial-close-month-picker > span { display: none; }
  .ph-toolbox .financial-close-month-picker input { min-width: 0; }
  /* El cluster ocupa la celda entera: si no, se queda en su ancho intrínseco
     y el mes sale recortado aunque sobre espacio a su derecha. */
  .ph-toolbox .header-period-nav,
  .ph-toolbox .finance-period-tools { min-width: 0; width: 100%; }
  .ph-toolbox .financial-close-month-picker { flex: 1 1 auto; min-width: 0; }

  /* Las 4 vistas contables declaran su período con un contenedor que también
     lleva .book-tools, y de ahí heredaba el ancho de columna de las barras de
     filtro (226px): dentro de la celda no cabían las flechas y se apilaban
     una arriba y otra abajo. Aquí el cluster manda sobre esa herencia. */
  .ph-toolbox .finance-period-tools {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 4px;
    min-width: 0;
  }
  .ph-toolbox .finance-period-tools > label { width: auto; min-width: 0; flex: 1 1 auto; }
  /* Mismas flechas compactas que .header-period-nav: con las de 40px el
     selector se quedaba sin ancho y el mes salía cortado ("enero de 20"). */
  .ph-toolbox .finance-period-tools > .icon-button {
    flex: none;
    width: 32px;
    min-height: var(--control-h-compact);
  }

  /* Trabajo diario mete cuatro controles en la celda —fecha, ‹, Hoy, ›— que
     suman 292px contra los 248 que hay: se apilaban en dos filas y la barra
     entera pasaba de 54 a 102px solo en esa vista. Mismo remedio que el
     navegador de mes: nada envuelve y los botones van compactos. */
  .ph-toolbox .daily-date-tools {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 4px;
    margin: 0;
  }
  /* El <input type="date"> nativo NO recorta con elipsis: si le falta ancho,
     el navegador se come el día por la izquierda y queda "08-2026". Nada de
     encogerlo — pide lo suyo y la celda de período crece para dárselo. */
  .ph-toolbox .daily-date-tools .date-input { flex: 0 0 auto; }
  .ph-toolbox .daily-date-tools > .icon-button {
    flex: none;
    width: 28px;
    min-height: var(--control-h-compact);
    padding: 0;
  }
  .ph-toolbox .daily-date-tools > .secondary-action {
    flex: none;
    min-height: var(--control-h-compact);
    padding: 0 9px;
  }

  /* Vista sin filtro de fechas: el compartimento se mantiene, apagado. */
  .ph-period--vacio { color: var(--muted-2); }
  .ph-period-nada {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: var(--fs-caption); font-weight: 650;
    letter-spacing: .06em; text-transform: uppercase;
    white-space: nowrap;
  }
  .ph-period-ayuda {
    width: 17px; height: 17px;
    display: grid; place-items: center;
    border: 1px solid var(--line-2); border-radius: 50%;
    color: var(--muted-2);
    font-size: var(--fs-caption); font-weight: 700; line-height: 1;
    cursor: help;
  }
  .ph-period-ayuda:hover { border-color: var(--accent); color: var(--accent); }

  /* 4 · Indicadores: la única celda elástica; ocupa lo que sobra al final de
     la barra. Su contenido cambia por vista, pero la celda no: los que no
     caben se alcanzan con scroll. */
  .ph-toolbox > .ph-stats {
    grid-column: 5;
    /* Fila explícita, o la barra se parte en dos: los indicadores van antes en
       el marcado pero ahora ocupan la columna 5, y cuando Filtros pide la 4 la
       auto-colocación —que nunca retrocede— lo manda a una fila nueva. */
    grid-row: 1;
    min-width: 0;
    justify-content: flex-start;
    padding: 6px 14px;
  }

  /* 3 · Filtros: junto al período, antes de los indicadores. Siempre
     presente: no desaparece cuando la vista no tiene filtros — se apaga, para
     que la barra conserve su forma y el sitio del botón sea siempre el mismo. */
  .ph-toolbox > .filtros-btn {
    /* Coordenada fija, no auto-colocación: las vistas sin indicadores ocultan
       .ph-stats, el grid corría el botón a esa columna (1fr) y lo estiraba a
       543px, comiéndose el hueco reservado para los KPI. Con la columna
       clavada, el sitio del botón —y el de los indicadores— no depende de que
       la vista tenga o no tenga. */
    grid-column: 4;
    grid-row: 1;
    align-self: stretch;
    justify-content: center;
    /* Pierde su borde de botón suelto pero CONSERVA el izquierdo: es la misma
       línea que separa a los demás compartimentos. Sin ella el último KPI del
       carrusel se pegaba a la palabra "Filtros" sin frontera visible. */
    border: 0;
    border-left: 1px solid color-mix(in srgb, var(--surface) 45%, transparent);
    border-radius: 0;
    padding: 0 16px;
  }

  /* v1 los usaba de riel decorativo; apagados explícitos para que ningún
     tema los reviva */
  .ph-title-group::before,
  .ph-title-group::after { display: none; }

  /* ── Indicadores dentro del toolbox: fila con scroll horizontal ── */
  .ph-stats {
    display: flex; align-items: center;
    gap: 22px;
    overflow-x: auto;
    scrollbar-width: thin;
    overscroll-behavior-x: contain;   /* el scroll no arrastra la página */
  }


  /* Dentro del toolbox nada lleva caja propia: la caja es el toolbox.
     Sin esto se ven marcos dentro de marcos. */
  .ph-toolbox :is(.icon-button, .ph-stat, .date-range-trigger) {
    border-color: transparent;
    background: transparent;
    box-shadow: none;
  }
  .ph-toolbox :is(.icon-button, .date-range-trigger):hover {
    background: var(--surface-2);
    border-color: transparent;
  }

  /* Chip solo lectura: hover/focus/active no aplican.
     Dos pisos, no tres: la nota explicativa (.ph-stat-sub) se lee una vez
     y después cuesta ~32px de alto en TODAS las vistas — se guarda como
     title para consulta puntual. */
  .ph-stat {
    flex: 0 0 auto;
    display: flex; flex-direction: column; align-items: flex-start;
    gap: 1px;
    padding: 0;
  }

  .ph-stat-value {
    font-family: var(--font-numeric);
    font-size: var(--fs-body-lg); font-weight: 800;   /* v1 15px → escala */
    letter-spacing: -.02em; line-height: 1.05;
    color: var(--ink);
    white-space: nowrap;   /* cifras (también negativas) en una línea */
    font-variant-numeric: tabular-nums;
  }

  .ph-stat--green .ph-stat-value { color: var(--green); }
  .ph-stat--amber .ph-stat-value { color: var(--amber); }
  .ph-stat--red   .ph-stat-value { color: var(--red); }

  .ph-stat-label {
    font-size: var(--fs-caption); font-weight: 650;
    letter-spacing: .04em; text-transform: uppercase;
    color: var(--muted); white-space: nowrap;
  }

  /* Tercer piso del chip: fuera del flujo. Su texto queda accesible como
     tooltip (lo pone app.js en el title del chip). */
  .ph-stat-sub { display: none; }

  /* ── Período: cluster ‹ mes › que JS reubica dentro de #phPeriod ── */
  .ph-period { display: flex; align-items: center; }
  .ph-period .date-range-filter { margin: 0; }

  .header-period-nav { display: flex; align-items: center; gap: 6px; }
  .header-period-nav .icon-button { width: 32px; min-height: var(--control-h-compact); }
  .header-period-nav .table-action { min-height: var(--control-h-compact); }
  .header-period-nav .pill { margin: 0; white-space: nowrap; }

  /* ── Selector de mes (label con <input type=month>). Modo vista (grid):
     caption arriba, input abajo — tipografía de label e input ya en 02-base ── */
  .financial-close-month-picker { position: relative; }
  .financial-close-month-picker input { min-width: 145px; }

  /* Overlay es-CL: el navegador pinta el mes en el idioma del OS ("January
     2026"); el texto nativo va transparente y encima attr(data-month-label)
     que escribe app.js (localizeMonthInputs). El picker sigue siendo nativo. */
  .financial-close-month-picker input[type="month"][data-mes-localizado] { color: transparent; }

  /* Anclaje 1 — vista (grid): sobre el input, que es la fila de abajo.
     right despeja el indicador nativo; uppercase/tracking del label se resetean */
  .financial-close-month-picker[data-month-label]:not([data-month-label=""])::after {
    content: attr(data-month-label);
    position: absolute;
    inset: auto 32px 0 12px;
    height: var(--control-h);
    display: flex; align-items: center;
    overflow: hidden; white-space: nowrap;
    pointer-events: none;
    font-size: var(--fs-body); font-weight: 550;
    letter-spacing: 0; text-transform: none;
    color: var(--ink);
  }

  /* Modo header (dentro de .header-period-nav): caption + input en fila */
  .header-period-nav .financial-close-month-picker {
    display: inline-flex; align-items: center; gap: 6px; margin: 0;
  }
  .header-period-nav .financial-close-month-picker span { white-space: nowrap; }
  .header-period-nav .financial-close-month-picker input {
    width: auto; min-width: 0;
    font-size: var(--fs-small); font-weight: 650;
  }

  /* Anclaje 2 — header (fila): el overlay se pega al input, a la derecha */
  .header-period-nav .financial-close-month-picker[data-month-label]:not([data-month-label=""])::after {
    inset: 50% 32px auto auto;
    height: auto; transform: translateY(-50%);
    font-size: var(--fs-small); font-weight: 650;
  }

  /* ── Acciones (campanita · actualizar · sesión). En el header los controles
     van planos: el chip lo da el borde, no la sombra ── */
  .top-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
  .ph-top .icon-button,
  .ph-top .date-range-trigger { box-shadow: none; }

  /* ── Actualizar: giro con carácter; is-busy corta clicks mientras carga ── */
  #refreshBtn [data-icon] { display: inline-flex; transition: transform .35s var(--ease-pop); }
  #refreshBtn:hover [data-icon] { transform: rotate(50deg); }
  #refreshBtn.is-busy { pointer-events: none; color: var(--ink); border-color: var(--ink-2); }
  #refreshBtn.is-busy [data-icon] {
    transition: none;
    animation: ph-refresh-spin .65s cubic-bezier(.55, .12, .45, .88) infinite;
  }
  #refreshBtn.is-done [data-icon] { animation: ph-refresh-done .55s cubic-bezier(.2, .8, .3, 1.2) both; }

  @keyframes ph-refresh-spin { from { transform: rotate(0); } to { transform: rotate(360deg); } }
  @keyframes ph-refresh-done {
    0% { transform: scale(1) rotate(0); }
    38% { transform: scale(1.28) rotate(24deg); }
    100% { transform: scale(1) rotate(0); }
  }

  /* (La campanita —botón, badge, animaciones de repique, panel— vive
     completa en feedback.css: allí también su reduced-motion.) */

  /* ── Respiro de los KPI al recalcular (solo si el usuario acepta motion) ── */
  @media (prefers-reduced-motion: no-preference) {
    .ph-stat { animation: ph-stat-in .18s var(--ease) backwards; }
    .ph-stat:nth-child(2) { animation-delay: .03s; }
    .ph-stat:nth-child(3) { animation-delay: .06s; }
    .ph-stat:nth-child(4) { animation-delay: .09s; }
    .ph-stat:nth-child(5) { animation-delay: .12s; }
  }
  @keyframes ph-stat-in { from { opacity: 0; } }

  @media (prefers-reduced-motion: reduce) {
    #refreshBtn [data-icon],
    #refreshBtn:hover [data-icon] { transition: none; transform: none; }
    #refreshBtn.is-busy [data-icon],
    #refreshBtn.is-done [data-icon] { animation: none; }
    #refreshBtn.is-busy { opacity: .55; }   /* el "cargando" queda dicho sin giro */
  }

  /* ── Responsive ────────────────────────────────────────────────────────
     En pantalla angosta el toolbox sí baja a su propia fila y toma el ancho
     completo: al lado del título ya no queda espacio útil para scrollear. */
  @media (max-width: 1100px) {
    .ph-top { flex-wrap: wrap; }
    .ph-toolbox { flex: 1 1 100%; margin-left: 0; }
  }

  @media (max-width: 900px) {
    .page-header { margin: -20px -20px 20px; }
    .ph-top { padding: 16px 20px; }
  }

  @media (max-width: 680px) {
    .ph-title { font-size: var(--fs-h2); }  /* v1 bajaba a 26/22px → escala */
    .ph-top { gap: 10px 12px; }
    .top-actions { flex-wrap: wrap; }
  }
}

/* líneas: 311 de presupuesto 250 (+61: la barra con sus cuatro
   compartimentos y el marcador de "sin período" no estaban en el encargo
   original. El duplicado de campanita contra feedback.css SÍ se resolvió:
   vive completa allá.) */
/* ══ panel ══════════════════════════════════════════════════════
   Cubre: .table-section, .split-grid, .overview-panel(+--wide),
          .overview-command-bar, .overview-tools (geometría en la barra),
          .report-panel(+-head/-actions), .report-main-panel/-side-panel,
          .section-head, .section-note, .overview-panel-head,
          .finance-overview-panel(+--wide), .finance-panel-head,
          .tool-panel, .accounting-panel, .accounting-basis-banner,
          .finance-period-banner, .finance-summary-list, .owner-account-reading,
          .report-board-head, .financial-entry-card, .chart-account-group,
          .widget-pin, .report-chart(+.menu-chart), .report-main-chart,
          .report-fallback-row
   Fuente v1: styles.css ~L2245-2252, ~L3300-3332, ~L4348-4510, ~L6546-6801,
              ~L7821, ~L9667-9689, ~L13475 y responsive ~L10269-10925,
              ~L14139-14188 (fusionadas)
   Muertas (grep en index.php/app.js, sin emisor en el repo): .card suelta
     (solo compuestas tipo *-card), .report-chart-canvas, .overview-module(-grid)
     (orquestador viejo del Resumen, jubilado por el dashboard configurable).
   Estados: paneles = superficie estática (interacción vive en sus controles);
     .report-fallback-row completo | sin disabled/loading/error (no aplica:
     nunca se deshabilita; carga, vacío y error los resuelve la vista).
   Cambios visibles: sin sombra permanente (receta canon), radios en --r
     (v117 subía report a --r-lg), padding report-panel 13→16, table-section
     18→16; tamaños fuera de escala al token más cercano (15→14.5, 11.5→12,
     9→11, 20→21).
   ═══════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── Superficie: LA receta, una sola vez ──────────────────────────────
     Blanco, 1px de línea, radio de tarjeta. Sin sombra permanente:
     la elevación se reserva para overlays (popover, modal). */
  .table-section,
  .overview-panel,
  .overview-command-bar,
  .report-panel,
  .finance-overview-panel,
  .tool-panel,
  .accounting-panel,
  .report-board-head,
  .financial-entry-card,
  .chart-account-group {
    min-width: 0;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r);
  }

  /* ── .table-section: panel contenedor de tabla + cabecera ───────────── */
  .table-section {
    /* En variable porque la tabla que cierra la tarjeta lo cancela con un
       margen negativo para salir a sangre (tabla.css). Una vista que quiera
       otro padding cambia --pad-tarjeta, no `padding`: si cambia solo el
       padding, el margen negativo deja de cuadrar y la tabla se sale del
       marco (le pasó a Cuentas por cobrar, que va a sangre entera). */
    --pad-tarjeta: 16px;
    padding: var(--pad-tarjeta);
    margin-top: 12px;
  }

  /* Superficies compartidas que v1 repetía en vistas operativas y
     contables. La geometría interna sigue perteneciendo a cada vista. */
  .tool-panel,
  .accounting-panel { padding: 16px; }
  .accounting-panel { margin-bottom: 16px; }
  .report-board-head { padding: 12px 16px; }

  .accounting-panel > summary,
  .financial-entry-card > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    cursor: pointer;
    list-style: none;
  }
  /* El acordeón de asiento ES la tarjeta (no vive dentro de un panel con
     padding propio): su cabecera pone el aire, o las filas se apelmazan. */
  .financial-entry-card > summary { padding: 12px 14px; }
  :is(.accounting-panel, .financial-entry-card) > summary::-webkit-details-marker { display: none; }
  :is(.accounting-panel, .financial-entry-card) > summary h3 { margin: 2px 0 4px; font-size: var(--fs-h3); }
  :is(.accounting-panel, .financial-entry-card) > summary span { color: var(--muted); font-size: var(--fs-caption); }

  /* ── Cabecera del asiento: número + glosa en una línea, fecha y origen
     en la de abajo. Sin esta grilla los tres textos se pegan y quedan como
     "202601-0001Asiento de apertura…2026-01-01 · Apertura · …". */
  .financial-entry-card > summary > div { display: flex; align-items: center; gap: 8px; min-width: 0; }

  .financial-entry-card > summary > div:first-child {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0 8px;
    align-items: baseline;
  }
  .financial-entry-card > summary > div:first-child strong {
    color: var(--accent);
    font-size: var(--fs-caption);
    font-variant-numeric: tabular-nums;
  }
  .financial-entry-card > summary > div:first-child span {
    overflow: hidden;
    color: var(--ink-2);
    font-size: var(--fs-caption);
    font-weight: 650;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* La fecha y el origen bajan a su propia línea, bajo las dos columnas. */
  .financial-entry-card > summary > div:first-child small {
    grid-column: 1 / -1;
    margin-top: 2px;
    color: var(--muted);
    font-size: var(--fs-caption);
  }
  .financial-entry-card > summary > div:last-child > strong {
    color: var(--ink);
    font-size: var(--fs-small);
    font-variant-numeric: tabular-nums;
  }
  .accounting-manual-entry[open] > summary { padding-bottom: 14px; border-bottom: 1px solid var(--line); }

  /* Plan de cuentas: tarjeta contenedora reutilizable del Libro mayor. */
  .chart-account-group { overflow: hidden; }
  .chart-account-group > div { padding: 12px 14px; border-bottom: 1px solid var(--line); background: var(--surface-2); }
  .chart-account-group > div small { color: var(--muted); font-size: var(--fs-caption); letter-spacing: .06em; text-transform: uppercase; }
  .chart-account-group h3 { margin: 4px 0 0; font-size: var(--fs-body); }
  .chart-account-group > button {
    display: grid; grid-template-columns: 62px 1fr; gap: 4px 10px;
    width: 100%; padding: 10px 14px; border: 0; border-bottom: 1px solid var(--line);
    background: transparent; color: var(--ink-2); text-align: left; cursor: pointer;
  }
  .chart-account-group > button:hover { background: var(--surface-2); }
  .chart-account-group > button strong { grid-row: 1 / 3; color: var(--accent); }
  .chart-account-group > button small { color: var(--muted); font-size: var(--fs-caption); }

  /* ── .section-head: cabecera de sección dentro de un panel ────────────
     Estructura vetada para fusión con el page-header: se queda como está.
     El h2 (17px) ya lo resuelve la base; aquí solo geometría. */
  .section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 14px;
  }

  /* El eyebrow del panel se calla: el sidebar marca la vista y el
     page-header ya trae el suyo. Tres veces lo mismo era ruido, y el
     tercero además empujaba el contenido hacia abajo. */
  .section-head .eyebrow,
  .report-panel-head .eyebrow { display: none; }

  /* El título nunca se comprime a cero cuando la toolbar aprieta. */
  .section-head > div:first-child { min-width: 200px; }

  /* Subtítulo de cabecera: LA receta, compartida por todas las cabeceras.
     Selectores estrechos a propósito: el `span` ancho de v1 alcanzaba a los
     spans de pills/selector de fechas y obligaba a re-overrides. El "-" de
     dato ausente llega del JS y se muestra tal cual; texto largo envuelve. */
  .section-note,
  .overview-command-bar > div:first-child > span,
  .report-panel-head > div:first-child > span,
  .finance-panel-head > div > span {
    display: block;
    /* La tabla puede usar toda la pantalla; la frase que la explica, no.
       Sin este tope la nota de sección se estiraba a 1.700px de una sola
       línea y el ojo perdía el renglón al volver. */
    max-width: 96ch;
    margin-top: 2px;
    color: var(--muted);
    font-size: var(--fs-small);
    font-weight: 550;
  }

  /* ── .split-grid: dos columnas contenido/lateral ────────────────────── */
  .split-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(320px, .65fr);
    gap: 12px;
    margin-bottom: 12px;
  }

  /* ── .overview-command-bar: barra de mando del Resumen ────────────────
     Barra densa: padding menor que la tarjeta estándar a propósito. */
  .overview-command-bar {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 14px;
    padding: 14px 16px;
  }

  .overview-command-bar > div:first-child { min-width: 0; }

  .overview-command-bar h2 { font-size: var(--fs-h2); }

  .overview-command-bar .overview-tools { flex: 0 0 auto; }

  /* ── .overview-panel: tarjeta del dashboard configurable ────────────── */
  .overview-panel {
    min-height: 300px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 16px;
  }

  .overview-panel--wide { min-height: 392px; }

  .overview-panel-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
  }

  .overview-panel-head > div { min-width: 0; }
  .overview-panel-head .eyebrow { margin-bottom: 4px; }

  /* ── .report-panel: panel de gráfico/ranking (Reportes, Menú, MoM) ──── */
  .report-panel {
    min-height: 0;
    padding: 16px;
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    gap: 10px;
  }

  .report-panel-head {
    min-width: 0;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
  }

  .report-panel-actions {
    display: flex;
    align-items: flex-end;
    justify-content: flex-end;
    gap: 6px;
    flex-wrap: wrap;
  }

  .report-panel-actions label { width: 168px; }

  /* El pill del período no se estira ni derrama en modo stretch. */
  .report-panel-actions .pill {
    align-self: center;
    width: auto;
    height: auto;
    white-space: nowrap;
  }

  /* .finance-overview-panel: padding mayor (18) que sostiene cifras grandes.
     --wide es grid-column por vista (dashboard/finance-overview-grid): no es
     geometría del componente, se deja a cada vista dueña de su grilla. */
  .finance-overview-panel { padding: 18px; }

  /* ── .finance-panel-head: cabecera con cifra protagonista ───────────── */
  .finance-panel-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 14px;
  }

  .finance-panel-head .eyebrow { margin-bottom: 4px; }
  .finance-panel-head h3 { font-size: var(--fs-h3); }

  /* Cifra: color por utilidades .is-positive/.is-negative (negativos). */
  .finance-panel-head > strong {
    font-size: var(--fs-h2);
    line-height: 1.15;
    text-align: right;
    font-family: var(--font-numeric);
    font-variant-numeric: tabular-nums;
  }

  .finance-panel-head > strong small {
    display: block;
    margin-top: 2px;
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 650;
    letter-spacing: .06em;
    text-transform: uppercase;
  }

  /* ── Contenedores de gráfico ──────────────────────────────────────────
     Superficie interna: mismo borde, radio de tarjeta; el overflow oculto
     evita que el canvas de la librería sangre sobre el radio. */
  .report-chart {
    min-height: 0;
    width: 100%;
    height: 100%;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
    overflow: hidden;
  }

  .report-main-panel { min-height: 640px; }
  .report-side-panel { min-height: 480px; }
  .report-main-chart { min-height: 560px; }

  /* Doble clase a propósito: gana por especificidad al height:100% de
     .report-chart. Alto fijo: el cuadrante del menú entra en una pantalla. */
  .report-chart.menu-chart { height: 460px; }

  /* ── .report-fallback-row: fila-tarjeta genérica (div o button) ─────── */
  .report-fallback-row {
    width: 100%;
    min-height: 54px;
    margin-bottom: 6px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
    color: var(--ink);
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 4px 10px;
    align-items: center;
    padding: 10px 12px;
    text-align: left;
    transition: background var(--t);
  }

  /* hover/active solo aportan en la variante button; en el div son inertes.
     focus-visible: lo cubre el outline de botón de la base. */
  .report-fallback-row:hover { background: var(--surface-2); }
  .report-fallback-row:active { background: var(--surface-3); }

  /* Texto largo: una línea con ellipsis (es una fila, no una tarjeta). */
  .report-fallback-row strong,
  .report-fallback-row span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .report-fallback-row span {
    color: var(--muted);
    font-size: var(--fs-small);
    font-weight: 550;
  }

  .report-fallback-row b {
    grid-row: 1 / span 2;
    grid-column: 2;
    font-size: var(--fs-body);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
  }

  /* ── Tarjetas de ingeniería de menú ──────────────────────────────── */
  .menu-opportunity {
    min-width: 0; display: flex; align-items: flex-start;
    justify-content: space-between; gap: 10px; padding: 10px 0;
    border-bottom: 1px solid var(--line);
  }
  .menu-opportunity:last-child { border-bottom: 0; }
  .menu-opportunity div { min-width: 0; }
  .menu-opportunity :is(span, small) { display: block; color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
  .menu-opportunity strong { display: block; margin: 2px 0; font-size: var(--fs-body); line-height: 1.25; }

  .menu-recommendation-brief,
  .menu-recommendation-card,
  .menu-quadrant-card {
    min-width: 0;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
  }
  .menu-recommendation-brief { grid-column: 1 / -1; display: grid; gap: 4px; padding: 12px; background: var(--surface-2); }
  .menu-recommendation-brief strong { font-size: var(--fs-body); }
  .menu-recommendation-brief span { color: var(--muted); font-size: var(--fs-small); line-height: 1.35; }

  .menu-recommendation-card { display: grid; gap: 10px; padding: 12px; border-left: 3px solid var(--line-2); }
  .menu-recommendation-card--star { border-left-color: var(--green); }
  .menu-recommendation-card--volume { border-left-color: var(--amber); }
  .menu-recommendation-card--puzzle { border-left-color: var(--blue); }
  .menu-recommendation-card--drain { border-left-color: var(--muted); }
  .menu-recommendation-main { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; align-items: start; }
  .menu-recommendation-main div { min-width: 0; }
  .menu-recommendation-main span { color: var(--muted); font-size: var(--fs-caption); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
  .menu-recommendation-main strong { display: block; margin-top: 2px; font-size: var(--fs-h3); line-height: 1.2; }
  .menu-recommendation-main p,
  .menu-recommendation-rationale { margin: 4px 0 0; color: var(--muted); font-size: var(--fs-small); line-height: 1.35; }
  .menu-recommendation-metrics { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; }
  .menu-recommendation-metrics small { min-width: 0; display: grid; gap: 4px; padding: 8px; border-right: 1px solid var(--line); color: var(--muted); font-size: var(--fs-caption); }
  .menu-recommendation-metrics small:last-child { border-right: 0; }
  .menu-recommendation-metrics b { color: var(--ink); font-size: var(--fs-small); white-space: nowrap; }
  .menu-recommendation-tags { display: flex; flex-wrap: wrap; gap: 6px; }
  .menu-recommendation-tags span { padding: 3px 8px; border-radius: 999px; background: var(--green-soft); color: var(--green); font-size: var(--fs-caption); font-weight: 700; }

  .menu-quadrant-card {
    width: 100%; min-height: 70px; display: grid;
    grid-template-columns: minmax(0, 1fr) auto; gap: 2px 10px;
    align-items: center; padding: 10px 12px; color: var(--ink); text-align: left;
  }
  .menu-quadrant-card:hover { border-color: var(--line-2); background: var(--surface-2); }
  .menu-quadrant-card span { min-width: 0; overflow: hidden; color: var(--muted); font-size: var(--fs-caption); font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
  .menu-quadrant-card strong { grid-column: 2; grid-row: 1; font-size: var(--fs-h2); line-height: 1; }
  .menu-quadrant-card small { grid-column: 1 / -1; overflow: hidden; color: var(--muted); font-size: var(--fs-caption); text-overflow: ellipsis; white-space: nowrap; }
  .menu-quadrant-card--star { border-left: 3px solid var(--green); }
  .menu-quadrant-card--volume { border-left: 3px solid var(--amber); }
  .menu-quadrant-card--puzzle { border-left: 3px solid var(--blue); }
  .menu-quadrant-card--drain { border-left: 3px solid var(--muted); }
  .menu-quadrant-card--uncosted { border-left: 3px solid var(--red); }
  .menu-quadrant-card--excluded { border-left: 3px solid var(--amber); }

  /* ── Banners financieros compartidos ─────────────────────────────── */
  .finance-period-banner,
  .accounting-basis-banner,
  .owner-account-reading {
    display: flex; align-items: center; justify-content: space-between;
    gap: 20px; margin-bottom: 16px; padding: 16px 18px;
    border: 1px solid var(--line); border-radius: var(--r);
    background: linear-gradient(135deg, var(--surface), var(--surface-2));
  }

  .finance-period-banner-main { display: flex; align-items: center; gap: 12px; min-width: 0; }
  .finance-period-banner-main > div { display: grid; gap: 4px; }
  .finance-period-banner-main strong { color: var(--ink); font-size: var(--fs-body); }
  .finance-period-banner-main small,
  .finance-source-coverage small { color: var(--muted); font-size: var(--fs-caption); line-height: 1.4; }
  .finance-source-coverage { flex: none; display: grid; min-width: 86px; text-align: right; }
  .finance-source-coverage strong { color: var(--ink); font-family: var(--font-numeric); font-size: var(--fs-h2); line-height: 1; }

  .accounting-basis-banner {
    border-color: color-mix(in srgb, var(--amber) 24%, transparent);
    background: var(--amber-soft);
  }
  .accounting-basis-banner.is-closed {
    border-color: color-mix(in srgb, var(--green) 24%, transparent);
    background: var(--green-soft);
  }
  .accounting-basis-banner > div { display: flex; align-items: center; gap: 10px; }
  .accounting-basis-banner strong { font-size: var(--fs-body); text-transform: capitalize; }
  .accounting-basis-banner p { max-width: 760px; margin: 0; color: var(--muted); font-size: var(--fs-caption); line-height: 1.45; text-align: right; }

  /* Los tintes de badge (8-16%) son demasiado sobre una superficie grande:
     este banner ocupa el ancho completo y el color debe insinuarse, no
     gritar. v1 lo resolvía con ~4% y era más amable — se conserva ese peso. */
  .owner-account-reading { border-left: 4px solid var(--green); background: var(--surface); }
  .owner-account-reading.is-debit {
    border-left-color: var(--amber);
    background: color-mix(in srgb, var(--amber-bright) 6%, var(--surface));
  }
  .owner-account-reading.is-credit {
    background: color-mix(in srgb, var(--green-vivo) 6%, var(--surface));
  }
  .owner-account-reading.is-zero {
    border-left-color: var(--blue);
    background: color-mix(in srgb, var(--accent) 4%, var(--surface));
  }
  .owner-account-reading small { color: var(--muted); font-size: var(--fs-caption); font-weight: 800; letter-spacing: .07em; text-transform: uppercase; }
  .owner-account-reading h3 { margin: 4px 0; font-size: var(--fs-h3); }
  .owner-account-reading p { max-width: 760px; margin: 0; color: var(--muted); font-size: var(--fs-small); line-height: 1.45; }
  .owner-account-reading > strong { flex: none; font-family: var(--font-numeric); font-size: var(--fs-title); }
  .owner-account-reading.is-debit > strong { color: var(--amber); }
  .owner-account-reading.is-credit > strong { color: var(--green); }

  /* Lista contable dt/dd compartida por Resumen y Flujo de caja. */
  .finance-summary-list { display: grid; margin: 0; }
  .finance-summary-list > div {
    display: flex; align-items: center; justify-content: space-between;
    gap: 18px; padding: 10px 0; border-top: 1px solid var(--line);
  }
  .finance-summary-list :is(dt, dd) { margin: 0; }
  .finance-summary-list dt { color: var(--ink-2); }
  .finance-summary-list dd { color: var(--ink); font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; }
  .finance-summary-list small { display: block; margin-top: 4px; color: var(--muted); font-size: var(--fs-caption); font-weight: 400; }

  /* Pin configurable de cualquier panel del dashboard. */
  .has-widget-pin { position: relative; }
  .has-widget-pin > :is(.overview-panel-head, .report-panel-head, .section-head) { padding-right: 40px; }
  .widget-pin {
    display: inline-grid; place-items: center; width: 32px; height: 32px;
    padding: 0; border: 1px solid var(--line); border-radius: 999px;
    background: var(--surface); color: var(--muted); cursor: pointer;
    transition: color var(--t), border-color var(--t), background var(--t), transform var(--t), opacity var(--t);
  }
  .widget-pin svg { width: 15px; height: 15px; }
  .widget-pin:hover { color: var(--accent); border-color: var(--accent); transform: translateY(-1px); }
  .widget-pin.is-pinned { background: var(--accent); border-color: var(--accent); color: var(--surface); }
  .widget-pin--corner { position: absolute; top: 12px; right: 12px; opacity: 0; }
  .has-widget-pin:hover .widget-pin--corner,
  .widget-pin--corner:is(:focus-visible, .is-pinned) { opacity: 1; }

  /* ── Responsive ─────────────────────────────────────────────────────── */
  @media (max-width: 1100px) {
    .report-chart.menu-chart { height: 420px; }
    .overview-panel--wide { min-height: 360px; }
  }

  @media (max-width: 900px) {
    .split-grid { grid-template-columns: 1fr; }

    /* La barra de mando se apila: título arriba, herramientas a lo ancho. */
    .overview-command-bar {
      display: grid;
      align-items: stretch;
    }
    .overview-command-bar .overview-tools { width: 100%; }

    .report-main-panel { min-height: 520px; }
    .report-side-panel { min-height: 440px; }
    .report-main-chart { min-height: 430px; }

    .finance-period-banner,
    .accounting-basis-banner,
    .owner-account-reading { align-items: flex-start; flex-direction: column; }
    .accounting-basis-banner p { text-align: left; }
    .menu-recommendation-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }

  @media (max-width: 680px) {
    .report-panel-head,
    .report-panel-actions { align-items: stretch; }

    .report-panel-actions label,
    .report-panel-actions button { width: 100%; }

    .overview-panel {
      min-height: 0;
      padding: 14px;
    }

    .overview-panel-head {
      flex-direction: column;
      align-items: stretch;
    }

    .overview-panel-head .pill,
    .overview-panel-head .table-action { align-self: flex-start; }
  }
}

/* líneas: ~300 de presupuesto 300 */
/* ══ popover · flotantes no-modales ═══════════════════════════════════════
   Cubre: .date-range-popover (+ .date-range-quick-list, .date-range-pane,
     .date-range-popover-head[--custom], .date-range-title-button,
     .date-range-selected-summary, .date-range-weekdays,
     .date-range-calendar-grid, .date-range-day[.is-muted|.is-in-range|
     .is-edge], .date-range-month-grid, .date-range-month-choice[.is-current|
     .is-selected], .date-range-actions),
     .account-help-dot, .account-help-popover (+ strong, p,
     .account-help-typical/-avoid)
   Fuente v1: styles.css ~L1776-1974 (date-range), ~L10561-10573 (responsive
     640px), ~L14259 (animación), ~L17804-17819 (account-help), fusionadas.
   NO cubre: .date-range-trigger[-text]/.date-range-filter (toolbar),
     .date-range-custom-grid (campo — rango manual), botones internos
     (.icon-button/.primary-action/.secondary-action → boton), .notif-panel
     (ya en feedback con el mismo z-popover). Sin menús dropdown propios en
     el contrato (grep "popover|menu" en app.js/index.php): el "+" del
     dashboard es un glifo sin flotante, los ".menu-*" son la vista de
     ingeniería de menú, ".orders-actions" es una fila de botones. Tampoco
     hay tooltips con clase CSS — los "tooltips" de v1 son config. de ECharts
     o el atributo title nativo. .date-range-month-jump (v1 ~L1960) verificada
     MUERTA — cero emisores en app.js/index.php, no se porta.
   Estados: reposo · entrada animada (popover-in) · hover en botones propios.
     focus-visible/disabled: botón base (02-base). Activo no suma feedback de
     prensado: el clic ya cambia de estado visible (.is-in-range/-edge/
     -selected). Disabled no aplica al calendario (.is-muted sigue clicable).
     Loading/error/vacío no aplican: ambos nacen con contenido ya calculado.
   Porte: z-index calc(var(--z-modal)-10) de v1 = 90 exacto → var(--z-popover).
     Fondo #fff → var(--surface). Padding asimétrico de account-help
     (13px 15px) se unifica a 14px parejo, mismo ritmo que el popover de
     fecha. lc-panel-in (compartida con modal en v1) se independiza aquí
     como popover-in para no acoplar archivos.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer componentes {
  @keyframes popover-in {
    from { opacity: 0; transform: translateY(10px) scale(.985); }
  }

  /* ── Date range: atajos a la izquierda, panel activo a la derecha ─────── */
  .date-range-popover {
    position: fixed;
    z-index: var(--z-popover);
    width: min(560px, calc(100vw - 24px));
    display: grid;
    grid-template-columns: 150px minmax(0, 1fr);
    gap: 14px;
    max-height: calc(100vh - 24px);
    overflow: auto;
    padding: 14px;
    border: 1px solid var(--line-2);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-md);
  }
  .date-range-popover:not([hidden]) { animation: popover-in .16s var(--ease); }

  .date-range-quick-list {
    display: grid;
    gap: 5px;
    align-content: start;
    padding-right: 12px;
    border-right: 1px solid var(--line);
  }
  .date-range-quick-list button {
    min-height: 30px;
    padding: 0 9px;
    text-align: left;
    font-size: var(--fs-small);
    font-weight: 650;
  }

  /* Botones "en blanco": atajos, días, meses y el título-navegador comparten
     reposo/hover; solo cambian de tamaño y de grilla contenedora. */
  .date-range-quick-list button,
  .date-range-day,
  .date-range-month-choice,
  .date-range-title-button {
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--ink-2);
  }
  .date-range-quick-list button:hover,
  .date-range-day:hover,
  .date-range-month-choice:hover,
  .date-range-title-button:hover {
    background: var(--surface-2);
    border-color: var(--line);
  }

  .date-range-pane { min-width: 0; display: grid; gap: 10px; }

  /* Cabecera de navegación: ‹ título › (el custom pierde el botón derecho) */
  .date-range-popover-head {
    display: grid;
    grid-template-columns: 32px 1fr 32px;
    align-items: center;
    gap: 8px;
  }
  .date-range-popover-head--custom { grid-template-columns: 32px 1fr; }

  .date-range-title-button {
    min-width: 0;
    min-height: 32px;
    padding: 0 10px;
    text-align: center;
    font-size: var(--fs-body-lg);
    font-weight: 800;
    color: var(--ink);
  }

  .date-range-selected-summary {
    min-height: 28px;
    padding: 6px 8px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    color: var(--muted);
    font-size: var(--fs-small);
    font-weight: 650;
  }

  /* ── Grilla de calendario: encabezado de días + 42 casillas ───────────── */
  .date-range-weekdays,
  .date-range-calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 4px;
  }
  .date-range-weekdays span {
    color: var(--muted-2);
    font-size: var(--fs-caption);
    font-weight: 700;
    text-align: center;
  }

  .date-range-day { aspect-ratio: 1; padding: 0; font-size: var(--fs-small); font-weight: 650; }
  .date-range-day.is-muted { color: var(--muted-2); }
  .date-range-day.is-in-range { background: var(--blue-soft); border-color: var(--blue-soft); color: var(--blue); }
  /* Extremo del rango elegido (from/to): tinta sólida, gana al azul suave */
  .date-range-day.is-edge { background: var(--ink); border-color: var(--ink); color: var(--surface); }

  /* ── Vista de meses (atajo "ver meses del año") ───────────────────────── */
  .date-range-month-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
    padding: 2px 0 4px;
  }
  .date-range-month-choice {
    min-height: 54px;
    padding: 0 8px;
    font-size: var(--fs-small);
    font-weight: 700;
    text-transform: capitalize;
  }
  .date-range-month-choice.is-current { border-color: var(--line-2); background: var(--surface-2); }
  .date-range-month-choice.is-selected { border-color: var(--ink); background: var(--ink); color: var(--surface); }

  .date-range-actions { display: flex; justify-content: flex-end; gap: 8px; }

  /* ── Account help: tarjeta angosta con guía de contrapartida contable ─── */
  .chart-account-group > button { position: relative; padding-right: 44px; }
  .account-help-dot {
    position: absolute;
    top: 50%;
    right: 12px;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border: 1px solid var(--line-2);
    border-radius: 50%;
    background: var(--surface);
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 650;
    line-height: 1;
    cursor: help;
    opacity: .62;
    transition: opacity var(--t), color var(--t), border-color var(--t), background var(--t);
  }
  .chart-account-group > button:hover .account-help-dot,
  .account-help-dot:focus-visible { opacity: 1; }
  .account-help-dot:hover,
  .account-help-dot.is-open {
    opacity: 1;
    color: var(--accent);
    border-color: var(--accent);
    background: var(--blue-soft);
  }

  .account-help-popover {
    position: fixed;
    z-index: var(--z-popover);
    width: min(330px, calc(100vw - 24px));
    max-height: calc(100vh - 24px);
    overflow: auto;
    padding: 14px;
    border: 1px solid var(--line-2);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-md);
  }
  .account-help-popover:not([hidden]) { animation: popover-in .16s var(--ease); }

  .account-help-popover strong { display: block; margin-bottom: 6px; color: var(--ink); font-size: var(--fs-small); }
  .account-help-popover p { margin: 0; color: var(--ink-2); font-size: var(--fs-small); line-height: 1.55; }
  .account-help-popover p + p { margin-top: 7px; }
  .account-help-popover p b {
    display: block;
    margin-bottom: 1px;
    font-size: var(--fs-caption);
    font-weight: 650;
    letter-spacing: .05em;
    text-transform: uppercase;
  }
  /* "Típico" queda en tinta tenue de apoyo; "Evítala" sube su etiqueta a
     ámbar de alerta — el cuerpo se queda legible, es un matiz, no un error */
  .account-help-popover .account-help-typical,
  .account-help-popover .account-help-typical b { color: var(--muted); }
  .account-help-popover .account-help-avoid { color: var(--ink-2); }
  .account-help-popover .account-help-avoid b { color: var(--amber); }

  /* ── Responsive (v1: 640px → 680px): fecha a 1 columna, atajos en 2 arriba */
  @media (max-width: 680px) {
    .date-range-popover { grid-template-columns: 1fr; }
    .date-range-quick-list {
      grid-template-columns: repeat(2, minmax(0, 1fr));
      padding-right: 0;
      padding-bottom: 12px;
      border-right: 0;
      border-bottom: 1px solid var(--line);
    }
  }
}

/* líneas: 209 de presupuesto 200 (+9: dos popovers completos con sus estados
   is-* propios — el calendario ya fusiona reposo/hover en un solo bloque
   compartido; no admite más recorte sin perder legibilidad) */
/* ══ tabla ══════════════════════════════════════════════════════
   Cubre: .table-wrap (+.flow-table-wrap), .numeric, redimensionamiento de
     columnas nativas (.resizable-column-head, .column-resize-handle,
     .table-column-resizing), .compact-table-wrap
     (variante densa reutilizable), .flow-table (+th/td base),
     .flow-section-row, .recipe-category-row, .recipe-category-toggle,
     .flow-row--subtotal, .flow-total-row, .mom-matrix-total,
     .accounting-table tfoot (trial-balance-table comparte clase),
     .flow-row--dim, .flow-row--meta, .mom-hist-wrap/.mom-hist-table,
     filas contables .is-draft, .table-action-row
   Fuente v1: styles.css ~L2610-2645 (redimensionamiento), ~L5051
     (table-wrap), ~L5096+L12581 (numeric),
     ~L9350 (compact-table-wrap, débil — reescrita con la intención de
     ~L12097-12143 .inv-year-table, hoy MUERTA sin emisor en DOM/JS),
     ~L9376-9490 (flow-table completo), ~L5316-5382 (recipe-category-*),
     ~L15930-15942 (accounting-table/trial-balance, solo tfoot),
     ~L1714-1719 + L5724 + L6255 (table-action-row, fusionadas).
     .mom-matrix-total es huérfana (v1 nunca la estilizó): se suma a la
     familia de fila-total porque su función en el DOM es idéntica.
   Fuera de alcance a propósito: .table-empty (completo en feedback.css),
     .table-action (en boton.css, aquí solo el contenedor fila),
     .compact-table-section (no aporta nada sobre .table-section de
     panel.css — ruido de v1, no se porta). Todo lo prefijado inv-
     (inv-summary-table, inv-tabulator-total-row, inv-editable…) resultó
     ser Tabulator real (verificado en app.js: vive dentro de divs
     #inventory*Table con Tabulator.js) — skin de otro agente; las únicas
     .inv-summary-total/.inv-year-table de v1 están MUERTAS y su intención
     de fila-total/densidad se reubicó en .flow-total-row/.mom-matrix-total
     y .compact-table-wrap. La transformación tabla→tarjeta en mobile
     (.recipe-table @620px) no se porta: geometría de columnas de una
     vista puntual (05-vistas/recetas si hace falta). .flow-val--*,
     .flow-month--current, .flow-nav-btn, .flow-payroll-*: coloreado
     semántico y navegación, no estructura — quedan para utilidades/botón.
   Estados: filas de sección/total/meta son informativas (hover/focus/
     active no aplican). .recipe-category-toggle sí es interactivo:
     reposo/hover/focus-visible completos; sin active/disabled/loading
     (alterna estado visual local, sin async). .table-action-row no es
     interactivo — contenedor flex de los .table-action reales (estados
     en boton.css).
   Contenidos límite: nombres largos en 1ª columna truncan por ellipsis
     donde v1 lo definía (fuera de esta capa); cifra negativa la colorea
     la utilidad semántica correspondiente, no esta capa; dato ausente
     llega como "—" de texto plano, ya alineado a la derecha por .numeric.
   ═══════════════════════════════════════════════════════════════ */

@layer componentes {

  /* ── .table-wrap: LA receta de contenedor de tabla ────────────────────
     .flow-table-wrap es el mismo contenedor con otro nombre en v1 (mismas
     4 propiedades) — se fusionan en una sola regla. */
  .table-wrap,
  .flow-table-wrap {
    width: 100%;
    overflow: auto;
    margin-top: 12px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
    /* Barras finas: en una tabla ancha la barra gruesa del sistema se come
       una franja de fila y pesa más que el dato que tapa. */
    scrollbar-width: thin;
    scrollbar-color: var(--line-2) transparent;
  }

  /* ── Ancho de columna: al contenido, con tope por celda ───────────────
     Una fila mide lo que mide su celda más alta, así que una fecha partida
     en "2026-" / "08-07" o un RUT en "77402885-" / "1" estiraba TODA la
     fila: Libro de compras pagaba 80px por registro donde caben 40 — la
     mitad de los registros visibles.

     Eso pasaba porque con `table-layout: auto` el navegador reparte el
     ancho disponible y aprieta las columnas cortas cuando el total no cabe.
     La solución no es prohibir el corte celda por celda (eso deja que una
     glosa de 50 caracteres estire la tabla a 3.700px), sino dimensionar la
     tabla a su contenido y poner el tope en la CELDA:

       · width: max-content  → cada columna recibe lo que pide, nunca se la
         aprieta; si sobra sitio, min-width:100% estira la tabla al marco.
       · max-width en la celda → lo único que envuelve es lo que de verdad
         es largo. Una fecha (80px) nunca llega al tope; una frase sí.

     Content-agnóstico: no hay que ir clasificando columnas a mano. */
  .table-wrap > table,
  .flow-table-wrap > table {
    width: max-content;
    min-width: 100%;
  }

  .table-wrap :is(th, td),
  .flow-table-wrap :is(th, td) { max-width: 340px; }

  /* Tipo de documento: el marcado trae "33" y su glosa separados por <br>,
     y esa segunda línea la pagaba la fila entera. En una sola línea la
     celda mide lo mismo que las demás. */
  td .doc-type ~ br { display: none; }
  td .doc-type ~ small::before { content: "·"; margin: 0 5px; color: var(--muted-2); }

  /* ── La tabla cierra su tarjeta a sangre ──────────────────────────────
     Con marco propio dentro del marco de la tarjeta se veía una caja
     redondeada dentro de otra, separadas por 16px: la tabla parecía
     flotar en su panel en vez de SER el panel. Cuando la tabla es lo
     último de la tarjeta se estira hasta sus bordes y hereda sus esquinas
     inferiores — un solo marco, y 32px más de ancho para las columnas.
     El borde superior se queda: es lo que separa la cabecera de la tabla. */
  .table-section > .table-wrap:last-child {
    width: auto;
    margin: 12px calc(var(--pad-tarjeta, 16px) * -1) calc(var(--pad-tarjeta, 16px) * -1);
    border-inline: 0;
    border-bottom: 0;
    border-radius: 0 0 calc(var(--r) - 1px) calc(var(--r) - 1px);
  }

  /* ── .numeric: alineación de cifra fuera de <td> (input, span) ────────
     td/th ya traen tabular-nums de la base; aquí cubre los casos donde
     .numeric cae sobre un elemento que la base no alcanza. */
  .numeric {
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
  }

  /* ── Redimensionamiento de columnas en tablas nativas ────────────────
     app.js monta el handle dentro del <th> y bloquea selección mientras
     arrastra. El <th> ya es sticky/posicionado en 02-base, por lo que es
     el bloque contenedor natural del handle. */
  .resizable-column-head {
    padding-right: 18px;
    user-select: none;
  }

  .column-resize-handle {
    position: absolute;
    inset: 0 -4px 0 auto;
    z-index: 1;
    width: 9px;
    cursor: col-resize;
    touch-action: none;
  }

  .column-resize-handle::after {
    content: "";
    position: absolute;
    inset: 8px 4px 8px auto;
    width: 1px;
    background: transparent;
    transition: background var(--t);
  }

  .resizable-column-head:hover .column-resize-handle::after,
  .table-column-resizing .column-resize-handle::after {
    background: var(--line-2);
  }

  body.table-column-resizing,
  body.table-column-resizing * {
    cursor: col-resize;
    user-select: none;
  }

  /* ── .compact-table-wrap: variante densa reutilizable ─────────────────
     Se activa junto a .table-wrap (`class="table-wrap compact-table-wrap"`).
     Reduce padding y tamaño de letra sobre la receta base de th/td —
     apunta a ~24px de fila, la misma altura que perseguía la vieja
     .inv-year-table sin repetir su deuda (table-layout fijo, 9 anchos de
     columna ad-hoc, z-index a mano). */
  .compact-table-wrap th,
  .compact-table-wrap td {
    padding: 4px 8px;
    font-size: var(--fs-small);
    line-height: 1.3;
  }

  /* ── .flow-table: matriz financiera densa (Flujo de caja) ─────────────
     Único caso vivo en v1 con columna izquierda fija; cifras a la derecha
     por defecto (es una matriz numérica, no una tabla de registros). */
  .flow-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-body);
  }

  .flow-table th,
  .flow-table td {
    padding: 8px 12px;
    text-align: right;
    white-space: nowrap;
    border-bottom: 1px solid var(--line);
  }

  .flow-table th {
    background: var(--surface-2);
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    border-bottom: 2px solid var(--line-2);
  }

  .flow-table th:first-child,
  .flow-table td:first-child { text-align: left; }

  /* Primera columna fija: único caso vivo de sticky horizontal en v1. */
  .flow-table td:first-child {
    position: sticky;
    left: 0;
    z-index: var(--z-sticky);
    background: var(--surface);
    color: var(--ink-2);
    font-weight: 400;
    border-right: 1px solid var(--line);
  }

  /* ── Filas de sección/grupo: banda fría que separa bloques ────────────
     La capa componentes ya gana a la fila-hover de base (layer anterior),
     así que el tinte se sostiene incluso al pasar el mouse — sin
     !important, a diferencia de v1. */
  .flow-section-row td,
  .recipe-category-row td {
    background: var(--surface-3);
    color: var(--ink-2);
    font-size: var(--fs-caption);
    font-weight: 700;
    text-transform: uppercase;
    border-top: 1px solid var(--line-2);
    border-bottom: 1px solid var(--line-2);
  }

  .flow-section-row td { letter-spacing: .08em; padding: 6px 12px; }

  /* .recipe-category-row aloja un botón que trae su propio padding, y
     queda pegada bajo el header de la tabla (sticky) al ser el separador
     de categorías de una tabla larga. */
  .recipe-category-row td {
    position: sticky;
    top: 33px;
    z-index: var(--z-sticky);
    padding: 0;
  }

  /* Disparador de la fila de categoría: icono +/− · nombre · métricas. */
  .recipe-category-toggle {
    width: 100%;
    min-height: var(--control-h);
    display: grid;
    grid-template-columns: 24px minmax(180px, auto) minmax(0, 1fr);
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border: 0;
    background: transparent;
    color: inherit;
    text-align: left;
    text-transform: none;
    cursor: pointer;
    transition: background var(--t);
  }

  /* v1 levantaba con un velo blanco translúcido; simplificado a un
     desplazamiento sólido hacia --surface (mismo efecto de "aclarar"). */
  .recipe-category-toggle:hover { background: var(--surface); }

  .recipe-category-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
  }

  .recipe-category-toggle > span {
    width: 22px;
    height: 22px;
    display: grid;
    place-items: center;
    border: 1px solid var(--line-2);
    border-radius: var(--r-xs);
    background: var(--surface);
    color: var(--ink);
    font-weight: 800;
    line-height: 1;
  }

  .recipe-category-toggle strong {
    min-width: 0;
    color: var(--ink);
    font-size: var(--fs-small);
    font-weight: 800;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .recipe-category-toggle small {
    min-width: 0;
    overflow: hidden;
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 550;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* ── Filas de subtotal/total: agrupa las equivalentes del contrato ────
     .flow-row--subtotal es el peldaño intermedio (línea fina); la fila de
     total propiamente tal —.flow-total-row, .mom-matrix-total (huérfana en
     v1) y el tfoot de .accounting-table (trial-balance-table comparte la
     clase)— comparte una sola voz: banda + borde grueso + negrita. */
  .flow-row--subtotal td {
    background: var(--surface-2);
    border-top: 1px solid var(--line-2);
    font-weight: 650;
  }

  .flow-total-row td,
  .mom-matrix-total td,
  .accounting-table tfoot td {
    background: var(--surface-2);
    border-top: 2px solid var(--line-2);
    font-weight: 700;
  }

  /* ── Tabla contable: la comparten Libro mayor, Cuentas por pagar y por
     cobrar, Situación financiera y Cuenta del propietario (NOTAS-P2 #11).
     Vive aquí y no en una vista porque ninguna de las cinco es su dueña. */
  .accounting-table { min-width: 850px; }

  .accounting-table th,
  .accounting-table td { text-align: left; vertical-align: middle; }

  /* La fecha del asiento es indivisible: partida en dos líneas
     ("2026-01-" / "31") desalinea toda la fila. */
  .accounting-table td:first-child { white-space: nowrap; }

  /* Las cuatro últimas columnas son cifras. */
  .accounting-table th:nth-last-child(-n+4),
  .accounting-table td:nth-last-child(-n+4) { text-align: right; }

  /* Celda apilada: el número de asiento arriba, su glosa debajo. Sin el
     bloque, el <small> se pega al botón y queda "202601-0001Asiento de…". */
  .accounting-table td small {
    display: block;
    margin-top: 2px;
    color: var(--muted);
    font-size: var(--fs-caption);
    line-height: 1.35;
  }

  /* ── Filas menores: atenuada y meta/porcentaje ────────────────────────
     Comparten familia con sección/total pero pesan menos: no son un
     quiebre de bloque ni una cifra protagonista. */
  .flow-row--dim td { color: var(--muted); }

  .flow-row--meta td {
    background: var(--surface-2);
    color: var(--muted);
    font-size: var(--fs-caption);
    padding: 4px 12px;
  }

  .flow-row--meta td:first-child { padding-left: 20px; font-style: italic; }

  /* ── .table-action-row: contenedor de acciones dentro de una tabla ────
     Puede llegar como <div> (barra sobre la tabla) o <td> (celda de
     acciones); en ambos casos es el mismo flex de botones alineados a la
     derecha. Variantes de alineación (columna, izquierda) son geometría
     de vista puntual, no se repiten aquí. */
  .table-action-row {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
  }

  /* ── Matrices históricas MoM / comportamiento de promociones ─────── */
  .mom-hist-wrap { overflow-x: auto; }
  .mom-hist-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-small);
    font-variant-numeric: tabular-nums;
  }
  .mom-hist-table thead th {
    padding: 6px 10px;
    border-bottom: 1px solid var(--line);
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 700;
    letter-spacing: .06em;
    text-align: right;
    text-transform: uppercase;
    white-space: nowrap;
  }
  .mom-hist-table thead th:first-child { text-align: left; }
  .mom-hist-table td {
    padding: 8px 10px;
    border-bottom: 1px solid var(--line);
    text-align: right;
    white-space: nowrap;
  }
  .mom-hist-label { min-width: 160px; color: var(--ink-2); font-weight: 550; text-align: left; }
  .mom-hist-pos { color: var(--green); font-weight: 650; }
  .mom-hist-neg { color: var(--red); font-weight: 650; }
  .mom-hist-zero { color: var(--muted); }
  .mom-hist-null { color: var(--muted-2); }

  /* Borradores compartidos por Libro mayor y Cuenta del propietario. */
  .accounting-table tr.is-draft td {
    background: var(--amber-soft);
    color: var(--muted);
  }

  /* ── Responsive ─────────────────────────────────────────────────────── */
  @media (max-width: 680px) {
    .flow-table th,
    .flow-table td { padding: 6px 8px; }
  }
}


/* líneas: 417 de presupuesto 400 (grande). El exceso son las tres recetas
   nuevas de densidad —ancho al contenido con tope por celda, tabla a sangre
   dentro de su tarjeta, tipo de documento en una línea— que sustituyen a
   min-widths y white-space repartidos por las vistas; el saldo neto del
   sistema baja. */
/* ══ toolbar · sistema de filtros ══════════════════════════════════════════
   Cubre: .book-tools, .sales-tools, .report-tools, .menu-tools,
     .overview-tools, .report-filter-strip, .report-filter-group,
     .report-filter-label, .consumption-filter-strip,
     .consumption-advanced-filters, .menu-filter-strip, .daily-date-tools,
     .daily-work-toolbar, .benchmark-tools, .receivables-tools,
     .coffee-calendar-toolbar, .catalog-photo-tools, .recipe-yield-tools,
     .settings-modal-toolbar, .annex-controls, .annex-file-control,
     .bank-movement-filter-bar,
     .segmented-control (+ .accounting-tabs, .finance-view-tabs, .orders-tabs,
     .settings-area-tabs), .coffee-event-product-modes,
     .coffee-quote-add-modes, .owner-account-filters, .hr-tabs, .report-chart-tabs,
     .report-chart-tabs-sep, .date-range-filter, .date-range-trigger,
     .date-range-trigger-text
   Fuente v1: styles.css ~L1721-1775, 1980-2020, 2139-2147, 3612-3775,
     4378-4384, 5421-5427, 6554-6584, 6707-6738, 7974-7980, 8389-8410, 9770,
     12479-12530 (incl. .annex-file-control L12512-12523),
     13508-13603 (v117), 14008-14012, 14218-14332 (v151/v154),
     14476, 15849, 16254-16270, 17095-17105, 18294-18300, 18360-18371,
     18746-18780 + hr.css L42-44, 581 (fusionadas).
   Estados: completo | loading no aplica (los filtros no cargan solos).
   Muertas verificadas (sin emisor en index.php/app.js): .report-filter,
     .report-filter-note — no se portan.
   Decisiones de porte visibles:
     · Los 6 grids ad-hoc de v1 (consumption, menu-strip, benchmark,
       receivables, recipe-yield, coffee) se unifican al ritmo flex-wrap;
       la geometría fina, si una vista la necesita, vuelve desde 05-vistas.
     · Segmentado: activo pasa de blanco a navy (canon report-chart-tabs);
       mismo peso 650 reposo/activo para que el ancho no salte.
     · Colapso animado v154 de [hidden] muere: exigía !important y el reset
       ya sanciona [hidden]{display:none}. Oculto = desaparece.
     · Sticky del .annex-controls (top 82px) es geometría del anexo → vista.
   ═════════════════════════════════════════════════════════════════════════ */

@layer componentes {

  .compact-search { min-width: 220px; }
  .settings-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
  /* ── Ritmo común: toda barra de filtros es una fila flex al ras ──────── */
  .book-tools,
  .sales-tools,
  .report-tools,
  .menu-tools,
  .overview-tools,
  .report-filter-strip,
  .consumption-filter-strip,
  .menu-filter-strip,
  .daily-date-tools,
  .daily-work-toolbar,
  .benchmark-tools,
  .receivables-tools,
  .coffee-calendar-toolbar,
  .recipe-yield-tools,
  .settings-modal-toolbar,
  .bank-movement-filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: 10px;
    min-width: 0;
  }

  /* Barras que viven junto al título de la vista: cuelgan a la derecha */
  .book-tools,
  .sales-tools,
  .report-tools,
  .menu-tools { justify-content: flex-end; }
  .menu-tools { flex: 1 1 560px; }
  .overview-tools { flex: 0 0 auto; }

  /* Barras que reparten identidad vs. control en los extremos */
  .coffee-calendar-toolbar,
  .settings-modal-toolbar,
  .bank-movement-filter-bar { justify-content: space-between; }

  /* Filas de acciones sin captions: centran, no alinean al ras */
  .coffee-calendar-toolbar,
  .daily-work-toolbar,
  .daily-date-tools { align-items: center; }

  .daily-work-toolbar,
  .coffee-calendar-toolbar { margin-bottom: 10px; }
  .recipe-yield-tools { margin: 12px 0; }

  /* ── Caption de columna: EN LÍNEA con su control, no encima ────────────
     La receta base de <label> apila (grid + gap 6), y en una barra de 8
     filtros que envuelve en 3 filas eso cuesta ~60px de alto por vista.
     En una herramienta que se usa el día entero ese alto es información
     perdida: el caption pasa al costado y la barra baja a una sola fila. */
  :is(.book-tools, .sales-tools, .report-tools, .menu-tools, .overview-tools,
      .report-filter-strip, .consumption-filter-strip,
      .consumption-advanced-filters, .menu-filter-strip, .daily-date-tools,
      .benchmark-tools, .receivables-tools, .recipe-yield-tools,
      .annex-controls, .catalog-photo-tools) > label {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--muted-2);
    white-space: nowrap;   /* el caption no se parte en dos líneas */
  }

  /* El control toma el ancho que quede tras el caption. */
  :is(.book-tools, .sales-tools, .report-tools, .menu-tools, .overview-tools,
      .report-filter-strip, .consumption-filter-strip,
      .consumption-advanced-filters, .menu-filter-strip, .daily-date-tools,
      .benchmark-tools, .receivables-tools, .recipe-yield-tools,
      .annex-controls, .catalog-photo-tools) > label > :is(select, input, .search-box) {
    flex: 1 1 auto;
    min-width: 0;
  }

  /* ── Anchos de columna: con el caption al costado la columna necesita
        más ancho útil que los 148px de la versión apilada ─────────────── */
  :is(.book-tools, .sales-tools, .report-tools, .menu-tools) > label { width: 226px; }

  :is(.book-tools, .sales-tools, .report-tools, .menu-tools, .overview-tools,
      .report-filter-strip, .consumption-filter-strip, .menu-filter-strip,
      .benchmark-tools, .receivables-tools) .search-box {
    flex: 1 1 220px;
    min-width: 200px;
  }

  /* El input de fecha del día no se estira a la fila entera */
  .daily-date-tools input[type="date"] {
    width: auto;
    min-width: 148px;
    flex: 0 0 auto;
  }

  /* Cobranza: el segmentado encabeza ocupando la fila, botones parejos */
  .receivables-tools .segmented-control { flex-basis: 100%; display: flex; }
  .receivables-tools .segmented-control button { flex: 1 1 0; }

  /* ── Strip de Reportes: única barra con caja propia ──────────────────── */
  .report-filter-strip {
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
    box-shadow: var(--shadow-xs);
  }

  /* Grupos contextuales (Ver por / Canal); [hidden] lo resuelve el reset */
  .report-filter-group {
    display: flex;
    align-items: end;
    gap: 8px;
    flex-wrap: wrap;
  }

  .report-filter-label {
    align-self: center;
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    white-space: nowrap;
  }

  /* La nota de alcance ocupa su propia fila al pie del strip */
  .report-filter-strip .report-scope-note {
    flex-basis: 100%;
    margin: 0;
    font-size: var(--fs-caption);
    line-height: 1.4;
  }

  /* ── Filtros avanzados de Consumo: <details> con verbo de pill ───────── */
  .consumption-advanced-filters { min-width: 0; }
  .consumption-advanced-filters[open] { flex-basis: 100%; }

  .consumption-advanced-filters summary {
    min-height: var(--control-h);
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 0 14px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--ink);
    font-weight: 650;
    box-shadow: var(--shadow-xs);
    cursor: pointer;
    list-style: none;
  }
  .consumption-advanced-filters summary:hover { border-color: var(--line-strong); }
  .consumption-advanced-filters summary::-webkit-details-marker { display: none; }
  .consumption-advanced-filters summary::after {
    content: "+";
    color: var(--muted);
    font-weight: 800;
  }
  .consumption-advanced-filters[open] summary::after { content: "−"; }

  /* ── Columnas de control (paneles laterales): pila, no fila ──────────── */
  .annex-controls { display: grid; gap: 12px; padding: 14px; }

  /* El aside del anexo cae en la receta de barra de arriba por su <label>,
     pero NO es una barra: es una columna de 286px. Con el caption al costado
     el <span> de estado del file input ("Base cargada: …") hereda nowrap, no
     puede encoger ni partirse, y estira la columna a 345px: el panel se monta
     encima de la tabla de insumos. Aquí vuelve a apilar, como hacía la v1. */
  .annex-controls > label {
    display: grid;
    gap: 5px;
    white-space: normal;
  }

  /* El file input crudo no cabe en los 40px de control-h; el span de estado
     ("Base cargada: …") es una frase, no una caption → escapa del uppercase
     que hereda de <label>. */
  .annex-file-control input { height: auto; padding: 8px 10px; }
  .annex-file-control span {
    font-size: var(--fs-small);
    font-weight: 650;
    color: var(--muted);
    letter-spacing: 0;
    text-transform: none;
  }

  .catalog-photo-tools { display: grid; gap: 8px; align-content: start; }
  .catalog-photo-tools small {
    color: var(--muted);
    font-size: var(--fs-caption);
    line-height: 1.4;
  }

  .settings-modal-toolbar { margin: 14px 0; }
  .settings-modal-toolbar .section-note { max-width: 620px; }

  /* ── Barra de movimientos bancarios: identidad + segmentado en caja ──── */
  .bank-movement-filter-bar {
    align-items: center;
    margin: 16px 0 12px;
    padding: 14px 16px;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-xs);
  }
  .bank-movement-filter-bar strong { display: block; font-size: var(--fs-body-lg); }
  .bank-movement-filter-bar > div > span {
    display: block;
    margin-top: 2px;
    color: var(--muted);
    font-size: var(--fs-caption);
  }
  .bank-movement-filter-bar .segmented-control { flex: 0 0 auto; }
  .bank-movement-filter-bar .segmented-control button span {
    display: inline;
    margin: 0 0 0 4px;
    font-size: inherit;
  }

  /* ── Segmentado: fondo banda, activo navy; .hr-tabs y .report-chart-tabs
        usan la misma receta ([data-accounting-tab] etc. son sus botones) ── */
  .segmented-control,
  .hr-tabs,
  .report-chart-tabs,
  .coffee-event-product-modes,
  .coffee-quote-add-modes {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 4px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface-2);
    height: var(--control-h);
    min-height: var(--control-h);
  }

  :is(.segmented-control, .hr-tabs, .report-chart-tabs,
      .coffee-event-product-modes, .coffee-quote-add-modes) button {
    height: var(--control-h-compact);
    border: 0;
    border-radius: var(--r-sm);
    background: transparent;
    color: var(--muted);
    padding: 0 12px;
    font-size: var(--fs-small);
    font-weight: 650; /* mismo peso activo/reposo: el ancho no salta */
    white-space: nowrap;
    max-width: 260px;
    cursor: pointer;
    transition: background var(--t), color var(--t), box-shadow var(--t);
  }
  /* focus-visible y disabled: los resuelve 02-base (anillo accent, opacidad) */
  :is(.segmented-control, .hr-tabs, .report-chart-tabs,
      .coffee-event-product-modes, .coffee-quote-add-modes) button:hover:not(.active):not(:disabled) {
    color: var(--ink);
    background: var(--surface);
  }
  :is(.segmented-control, .hr-tabs, .report-chart-tabs,
      .coffee-event-product-modes, .coffee-quote-add-modes) button.active {
    background: var(--ink);
    color: var(--surface);
    box-shadow: var(--shadow-xs);
  }

  /* Sabor píldora para las tabs de gráfico */
  .report-chart-tabs { border-radius: 999px; }
  .report-chart-tabs button {
    border-radius: 999px;
    font-size: var(--fs-caption);
    font-weight: 700;
  }
  .report-chart-tabs-sep {
    width: 1px;
    align-self: stretch;
    margin: 2px 6px;
    background: var(--line);
  }

  /* Variantes de segmentado (clase compuesta en el mismo elemento) */
  .accounting-tabs { margin: 0 0 16px; }
  .settings-area-tabs { margin-bottom: 14px; }
  .orders-tabs { width: fit-content; margin-bottom: 12px; }
  .finance-view-tabs { flex: none; }

  .coffee-event-product-modes,
  .coffee-quote-add-modes { width: fit-content; max-width: 100%; }

  /* Filtros contables: botones-pill con contador, no segmentado unido. */
  .owner-account-filters { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
  .owner-account-filters button {
    display: inline-flex; align-items: center; gap: 8px; min-height: 34px;
    padding: 6px 10px; border: 1px solid var(--line); border-radius: 999px;
    background: var(--surface); color: var(--muted); font-size: var(--fs-caption);
    font-weight: 700; cursor: pointer;
  }
  .owner-account-filters button:hover,
  .owner-account-filters button.active { border-color: var(--ink); background: var(--ink); color: var(--surface); }
  .owner-account-filters button span {
    min-width: 22px; padding: 2px 6px; border-radius: 999px;
    background: var(--surface-2); color: var(--muted); text-align: center;
  }
  .owner-account-filters button.active span {
    background: color-mix(in srgb, var(--surface) 14%, transparent);
    color: inherit;
  }

  /* .hr-tabs: nav de secciones RRHH — misma piel, fila completa y sticky */
  .hr-tabs {
    display: flex;
    position: sticky;
    top: 10px;
    z-index: var(--z-sticky);
  }
  .hr-tabs button { flex: 1 1 auto; max-width: none; }

  /* ── Período: .date-range-filter + botón .date-range-trigger ─────────── */
  .date-range-filter { min-width: 200px; }
  .overview-tools .date-range-filter { min-width: 260px; }

  .date-range-trigger {
    width: 100%;
    min-height: var(--control-h);
    padding: 0 12px;
    display: inline-flex;
    align-items: center;
    justify-content: flex-start;
    gap: 8px;
    border: 1px solid var(--line-2);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--ink-2);
    box-shadow: var(--shadow-xs);
    text-align: left;
    cursor: pointer;
    transition: background var(--t), border-color var(--t), box-shadow var(--t);
  }
  .date-range-trigger:hover:not(:disabled) {
    background: var(--surface-2);
    border-color: var(--line-strong);
  }
  /* focus-visible/disabled: 02-base; abierto = borde de interacción */
  .date-range-trigger[aria-expanded="true"] { border-color: var(--accent); }

  .date-range-trigger-text { min-width: 0; display: grid; gap: 2px; }
  .date-range-trigger-text small {
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 650;
    line-height: 1;
  }
  .date-range-trigger-text strong {
    color: var(--ink);
    font-size: var(--fs-small);
    font-weight: 700;
    line-height: 1.15;
    /* período largo ("1 ene — 31 dic 2026"): elipsis, nunca salto */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* ── Movimiento: feedback al presionar y pop al quedar activo ────────── */
  @media (prefers-reduced-motion: no-preference) {
    :is(.segmented-control, .hr-tabs, .report-chart-tabs,
        .coffee-event-product-modes, .coffee-quote-add-modes) button {
      transition: background var(--t), color var(--t), box-shadow var(--t),
        transform .12s var(--ease);
    }
    :is(.segmented-control, .hr-tabs, .report-chart-tabs,
        .coffee-event-product-modes, .coffee-quote-add-modes) button:active:not(:disabled) {
      transform: scale(.96);
    }
    :is(.segmented-control, .hr-tabs, .report-chart-tabs,
        .coffee-event-product-modes, .coffee-quote-add-modes) button.active {
      animation: toolbar-seg-pop .16s var(--ease-pop);
    }
  }
  @keyframes toolbar-seg-pop {
    0% { transform: scale(.95); }
    65% { transform: scale(1.02); }
    100% { transform: none; }
  }

  /* ── Responsive: 900 sueltan la derecha; 680 pila a fila completa ────── */
  @media (max-width: 900px) {
    .book-tools,
    .sales-tools,
    .report-tools,
    .menu-tools { justify-content: flex-start; }
  }

  @media (max-width: 680px) {
    :is(.book-tools, .sales-tools, .report-tools, .menu-tools,
        .overview-tools, .report-filter-strip, .consumption-filter-strip,
        .menu-filter-strip, .daily-date-tools, .daily-work-toolbar,
        .benchmark-tools, .receivables-tools, .recipe-yield-tools)
      > :is(label, button, .search-box, .date-range-filter, select, input) {
      width: 100%;
      min-width: 0;
      max-width: 100%;
      flex: 1 1 100%;
    }
    .daily-date-tools input[type="date"] { min-width: 0; }

    .segmented-control,
    .report-chart-tabs,
    .coffee-event-product-modes,
    .coffee-quote-add-modes {
      width: 100%;
      height: auto;
      min-height: var(--control-h);
      flex-wrap: wrap;
    }
    :is(.segmented-control, .hr-tabs, .report-chart-tabs,
        .coffee-event-product-modes, .coffee-quote-add-modes) button {
      flex: 1 1 auto;
      min-width: 0;
      max-width: 100%;
      padding: 0 8px;
    }
    .hr-tabs { flex-wrap: wrap; height: auto; }
    .orders-tabs { width: 100%; }

    .bank-movement-filter-bar { flex-direction: column; align-items: stretch; }
    .bank-movement-filter-bar .segmented-control { flex: 1 1 auto; }
  }
}

/* líneas: 401 de presupuesto 250 (+151: el sistema real son 23 contenedores
   de toolbar del contrato + segmented-control/hr-tabs/report-chart-tabs +
   date-range-filter/trigger fusionados desde >20 puntos dispersos de v1;
   ningún bloque duplica 02-base ni re-skinea desde vistas — encaja mejor en
   el techo "grande" ≤400 que en "mediano"; el footer previo (~248) estaba
   desactualizado, no reflejaba el archivo real) */
/* ══ activos-fijos — data-view="fixed-assets" ═══════════════════════════════
   Cubre: #view-fixed-assets .financial-operations-head, .financial-operation-
     grid (grilla del formulario + reset de .span-2 a 900px), .financial-
     operation-actions, .financial-operation-table-wrap/-table (ancho mínimo,
     alineación de columnas de cifra, celda apilada strong+small),
     .financial-operations-kpis (ancho de subtítulo).
   Fuente v1: styles.css ~L15836 (financial-operations-head), ~L16146-16251
     (bloque completo "operación financiera": kpis/form/grid/actions/table),
     ~L17373-17375 (quiebre del formulario a 900px, no a 680).
   NO cubre — ya resuelto arriba, no se repite (CONVENCIONES §8):
     .book-tools/.header-period-nav (toolbar.css/page-header.css);
     .financial-close-month-picker (page-header.css); .finance-panel-head/
     .section-head/.section-note (panel.css); .finance-status-badge
     (badge.css); .financial-close-kpis/.financial-blockers/.financial-
     close-section — declaradas sueltas (sin `#view-`) en cierre-del-mes.css
     porque esa vista es su dueña de identidad visual; alcanzan también a
     ésta sin que yo las repita; label/input/select/textarea del formulario
     (02-base.css) — `.financial-operation-grid` aquí SOLO fija la grilla,
     nunca su tipografía.
   Nota — `.financial-operation-grid`/`.financial-operations-head`/
     `.financial-operation-actions`/`.financial-operation-table(-wrap)` se
     comparten con Cartolas bancarias (mismas clases, ver
     05-vistas/cartolas-bancos.css): cada vista declara su propia copia
     scopeada a su `#view-*` — así ninguna geometría se filtra a la otra
     (mismo criterio que `.accounting-table` en cuentas-por-pagar.css).
   Nota — quiebre del formulario a 900px, no 680 (NOTAS-P2 #7): v1 colapsa
     `.financial-operation-grid` en `@media 900px` (L17373-17375), distinto
     del resto de grillas de 2 columnas del canon que la utilidad `.span-2`
     resuelve genéricamente en 680px; se respeta esa intención real con un
     reset local — sin él, entre 680-900px "Nombre del activo"/"Notas"
     (span-2) abrirían una columna implícita fantasma sobre una grilla que
     ya es de 1 columna.
   Casos límite: nombre del activo largo envuelve dentro de la celda (sin
     ancho fijo ni ellipsis, igual que v1); fecha de servicio/depreciación
     ausentes llegan como "—" de texto plano ya resuelto por JS, sin clase
     que posicionar; cifra negativa no aplica — ninguna fila de esta tabla
     emite `.is-positive`/`.is-negative` (verificado en app.js); sin activos
     registrados → fila con colspan sobre `.empty` (feedback.css, sin regla
     aquí); periodo cerrado → `.financial-blockers` antes de los KPIs, ya
     espaciado por su propio margin-bottom (cierre-del-mes.css).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Cabecera: título arriba-izquierda, período + acción abajo-derecha ── */
  #view-fixed-assets .financial-operations-head { align-items: flex-end; margin-bottom: 14px; }

  /* ── Ficha del bien: grilla de 2 columnas; Nombre/Notas van a lo ancho
     (.span-2, utilidades.css) ─────────────────────────────────────────── */
  #view-fixed-assets .financial-operation-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px 16px;
    padding-top: 16px;
  }
  #view-fixed-assets .financial-operation-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
  }

  /* ── Auxiliar permanente: 6 columnas, cifra a la derecha ──────────────── */
  #view-fixed-assets .financial-operation-table-wrap { overflow-x: auto; }
  #view-fixed-assets .financial-operation-table { min-width: 860px; }
  /* Costo y depreciación quedan a la IZQUIERDA (como v1): son celdas
     apiladas —monto arriba, "Residual $240.000" debajo— y alinearlas a la
     derecha desalinea las dos líneas entre sí. La regla de columnas
     numéricas a la derecha aplica a cifras sueltas, no a estas. */

  /* Celda apilada dato+metadato (código/categoría, vida útil, residual,
     valor libro): sin dueño en tabla.css, identidad mínima local. */
  #view-fixed-assets .financial-operation-table td strong,
  #view-fixed-assets .financial-operation-table td small { display: block; }
  #view-fixed-assets .financial-operation-table td small {
    margin-top: 2px;
    color: var(--muted);
    font-size: var(--fs-caption);
  }

  /* Subtítulo de KPI: el de "Costo histórico" es una frase larga cuando el
     valor libro está pendiente; se fuerza el wrap para que las 4 tarjetas
     mantengan la misma altura entre sí. */
  #view-fixed-assets .financial-operations-kpis article span { max-width: 34ch; }

  /* ── Responsive: ver Nota — este formulario colapsa en 900px, no en 680 ── */
  @media (max-width: 900px) {
    #view-fixed-assets .financial-operation-grid { grid-template-columns: 1fr; }
    #view-fixed-assets .financial-operation-grid .span-2 { grid-column: auto; }
  }
}

/* líneas: ~93 de presupuesto 150 */
/* ══ agenda (calendario coffee) ═════════════════════════════════════════════
   Vista: #view-calendar (data-view="calendar"). Calendario mensual con
   navegación de mes propia + semana/3 días/día (misma grilla, modo lista con
   línea de tiempo) + panel de detalle del día + modal de evento.
   Cubre: #view-calendar, .coffee-calendar-section/-layout/-panel/
     -detail-panel/-nav(+-arrow/-month-pill/-today-btn)/-head/-grid(+--agenda)/
     -day(+--blank/.is-selected/.is-today/.has-events)/-day-top/-day-number/
     -day-events/-day-event/-empty/-detail-head/-events/-selected-empty/
     -agenda(+--single)/-agenda-day(+.is-selected)/-agenda-day-head/-timeline/
     -time-slots/-time-slot/-time-label/-time-events/-time-event-layer/
     -time-event/-task-panel/-event-card/-event-head/-event-meta/-event-
     actions/-event-kind--{coffee,meeting,production,delivery,admin,other};
     .coffee-event-status-row (solo como hijo de .coffee-calendar-event-card);
     .calendar-event-products/-notes/-actions (retícula del modal).
   Fuente v1: styles.css ~L7964-8360 (layout/panel/nav/head/grid/timeline),
     ~L8082-8109 (retícula del modal), ~L8860, ~L8957-9348 (task-panel/día/
     kind/detalle/tarjeta/responsive), ~L13702 (hide del resumen KPI),
     ~L10384-10386, ~L10561-10716 (responsive 620/760/980/1180 → 680) —
     fusión last-wins.
   Fuera de alcance (ya resuelto en 04-componentes, no se repite aquí):
     .coffee-calendar-toolbar/-actions/-view-mode → toolbar.css (flex-wrap +
     segmented-control genéricos); .coffee-event-status/.coffee-calendar-
     task-chip → badge.css; ancho/alto/scroll/animación de .calendar-event-
     modal y las 3 columnas de .modal-grid → modal.css (.calendar-event-grid
     pedía las mismas 3 columnas que la base: sin regla propia).
   Pendiente de dueño (NO se porta aquí): .coffee-event-product-picker y su
     familia (-modes/-add/-list/-row/-total) — el mismo markup lo usa el
     modal de coffee break de #view-coffee; no es exclusivo de esta vista.
   Casos límite: mes sin eventos (.coffee-calendar-empty a columna completa);
     día con más de 2 eventos (chip "+N más", ya vive en badge.css); nombre
     de empresa largo (ellipsis en -event-head strong); rango horario amplio
     (el timeline crece con --hour-count, sin tope); día sin metadata (los
     spans de conteo se auto-ocultan con :empty).
   Cambios de porte documentados: el filete de "hoy" pasa de degradé+hex
     plano a --amber-soft/--amber; sombras/bordes rgba crudos → familias
     --shadow-* y --line-*; cifras y espaciados fuera de escala (20/10.5/10/
     9.5/9/7/5/3/1px) suben al peldaño permitido más cercano; tono "admin"
     (v1 #7c3aed) → --violet, tono "otro" (v1 #4b5563) → --muted/--surface-3;
     se retira la coreografía de entrada escalonada (calendarSurfaceIn/
     ChipIn/nth-child delays y calendarModalIn, ya unificado en modal.css) —
     la vista sigue animando hover/selección con --t, la entrada no aportaba
     información.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  .coffee-calendar-actions { justify-content: flex-end; }
  .coffee-calendar-view-mode button:not(.active) {
    border-color: transparent;
    background: transparent;
    color: var(--muted);
    box-shadow: none;
  }
  .coffee-calendar-view-mode button.active {
    border-color: var(--ink);
    background: var(--ink);
    color: var(--surface);
  }

  /* Vista de pantalla completa: sin page-header (data-view="calendar" lo
     apaga en page-header.css) ni resumen KPI propio — el mes necesita todo
     el alto disponible. 72px = padding vertical de .workspace (24 + 48). */
  #view-calendar .coffee-calendar-summary { display: none; }
  .coffee-calendar-section { min-height: calc(100dvh - var(--nav-alto) - 56px); }
  .coffee-calendar-detail-head { display: none; }   /* título repetido: el eyebrow del panel ya lo dice */

  /* ── Esqueleto: mes arriba (alto), detalle del día abajo ──────────────── */
  .coffee-calendar-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; align-items: start; }
  .coffee-calendar-panel {
    display: grid; grid-template-rows: auto auto minmax(0, 1fr);
    min-height: calc(100dvh - var(--nav-alto) - 122px);   /* descuenta además la barra de herramientas de arriba */
    padding: 12px;
  }
  .coffee-calendar-detail-panel { padding: 0; border: 0; background: transparent; box-shadow: none; }

  /* ── Navegación de mes propia (no header-period-nav global) ───────────── */
  .coffee-calendar-nav { display: flex; align-items: center; gap: 6px; margin-bottom: 12px; }
  .coffee-calendar-nav-arrow { font-size: var(--fs-h2); font-weight: 400; line-height: 1; }
  .coffee-calendar-month-pill { flex: 1; justify-content: center; pointer-events: none; }
  .coffee-calendar-today-btn { margin-left: auto; }

  /* ── Grilla de 7 columnas: cabecera de días + celdas del mes ──────────── */
  .coffee-calendar-head,
  .coffee-calendar-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 8px; }
  .coffee-calendar-head { margin-top: 10px; }
  .coffee-calendar-head span { color: var(--muted); font-size: var(--fs-caption); font-weight: 700; text-align: center; text-transform: uppercase; }
  .coffee-calendar-grid { margin-top: 8px; }
  .coffee-calendar-grid:not(.coffee-calendar-grid--agenda) { grid-auto-rows: minmax(118px, 1fr); }
  .coffee-calendar-grid--agenda { display: block; }   /* mismo contenedor sirve a semana/3 días/día */

  /* ── Celda de día ───────────────────────────────────────────────────────
     is-selected comparte anillo con .coffee-calendar-agenda-day (mismo
     significado: "este es el día activo" en cualquier modo de vista). */
  .coffee-calendar-day,
  .coffee-calendar-day--blank { min-height: 126px; border-radius: var(--r); }
  .coffee-calendar-day {
    min-width: 0; display: grid; align-content: start; gap: 8px;
    border: 1px solid var(--line); padding: 8px; background: var(--surface);
    text-align: left; transition: border-color var(--t), box-shadow var(--t), transform var(--t);
  }
  .coffee-calendar-day:hover { border-color: var(--line-2); box-shadow: var(--shadow-sm); transform: translateY(-1px); }
  .coffee-calendar-day:active { transform: translateY(0); }
  .coffee-calendar-day--blank { background: transparent; }
  .coffee-calendar-day.is-today { background: var(--amber-soft); border-color: var(--amber); }
  .coffee-calendar-day.has-events { border-left: 3px solid var(--green); }
  :is(.coffee-calendar-day, .coffee-calendar-agenda-day).is-selected { border-color: var(--ink); box-shadow: 0 0 0 2px var(--line-strong); }

  .coffee-calendar-day-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 6px; }
  .coffee-calendar-day-number { color: var(--ink); font-size: var(--fs-h2); font-weight: 800; line-height: 1; }
  .coffee-calendar-day-top > span:last-child,
  .coffee-calendar-day > small { color: var(--muted); font-size: var(--fs-caption); font-weight: 700; }
  .coffee-calendar-day-top > span:last-child:empty,
  .coffee-calendar-day > small:empty { display: none; }   /* día sin eventos: sin metadata vacía */
  .coffee-calendar-day-events { display: grid; gap: 6px; margin-top: auto; }

  /* ── Color por tipo de evento: mismas 2 variables las lee el chip del día,
     el evento de la línea de tiempo y la tarjeta de detalle. */
  .coffee-calendar-day-event,
  .coffee-calendar-time-event,
  .coffee-calendar-event-card { --calendar-event-color: var(--green); --calendar-event-bg: var(--green-soft); }
  .coffee-calendar-event-kind--meeting    { --calendar-event-color: var(--blue);   --calendar-event-bg: var(--blue-soft); }
  .coffee-calendar-event-kind--production { --calendar-event-color: var(--amber);  --calendar-event-bg: var(--amber-soft); }
  .coffee-calendar-event-kind--delivery   { --calendar-event-color: var(--teal);   --calendar-event-bg: var(--teal-soft); }
  .coffee-calendar-event-kind--admin      { --calendar-event-color: var(--violet); --calendar-event-bg: var(--violet-soft); }
  .coffee-calendar-event-kind--other      { --calendar-event-color: var(--muted);  --calendar-event-bg: var(--surface-3); }

  .coffee-calendar-day-event {
    display: grid; gap: 2px; min-width: 0; border-radius: var(--r-sm); padding: 4px 6px;
    background: var(--calendar-event-bg); color: var(--calendar-event-color); cursor: pointer;
    transition: box-shadow var(--t), transform var(--t);
  }
  .coffee-calendar-day-event:hover { box-shadow: var(--shadow-xs); transform: translateY(-1px); }
  .coffee-calendar-day-event strong,
  .coffee-calendar-day-event small { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .coffee-calendar-day-event strong { font-size: var(--fs-caption); font-weight: 700; }
  .coffee-calendar-day-event small { color: inherit; font-size: var(--fs-caption); font-weight: 650; }

  .coffee-calendar-empty { grid-column: 1 / -1; border: 1px dashed var(--line-2); border-radius: var(--r); padding: 16px; color: var(--muted); background: var(--surface-2); }

  /* ── Semana / 3 días / día: columnas de agenda con línea de tiempo ────── */
  .coffee-calendar-agenda { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: 10px; }
  .coffee-calendar-agenda--single { grid-template-columns: 1fr; }   /* vista "Día": una columna a ancho completo */
  .coffee-calendar-agenda-day { min-width: 0; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); overflow: hidden; }
  .coffee-calendar-agenda-day:hover { border-color: var(--line-2); box-shadow: var(--shadow-sm); }
  .coffee-calendar-agenda-day-head {
    width: 100%; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 8px; align-items: center;
    border: 0; border-bottom: 1px solid var(--line); padding: 10px 12px; background: var(--surface); text-align: left;
  }

  .coffee-calendar-timeline { position: relative; min-height: calc(var(--hour-count) * var(--slot-height)); }   /* JS inyecta ambas vars por el rango horario del período */
  .coffee-calendar-time-slots { display: grid; grid-template-rows: repeat(var(--hour-count), var(--slot-height)); }
  .coffee-calendar-time-slot { display: grid; grid-template-columns: 48px minmax(0, 1fr); border-bottom: 1px solid var(--line-soft); cursor: pointer; }
  .coffee-calendar-time-slot:last-child { border-bottom: 0; }
  .coffee-calendar-time-label { padding: 8px; color: var(--muted); font-size: var(--fs-caption); font-weight: 700; }
  .coffee-calendar-time-events { min-width: 0; border-left: 1px solid var(--line-soft); transition: background var(--t); }
  .coffee-calendar-time-slot:hover .coffee-calendar-time-events { background: color-mix(in srgb, var(--accent) 4%, transparent); }
  .coffee-calendar-time-event-layer { position: absolute; inset: 0 6px 0 54px; pointer-events: none; }   /* 54px = columna de horas (48px) + su gutter (6px) */
  .coffee-calendar-time-event {
    position: absolute; top: var(--event-top); left: var(--event-left); width: var(--event-width); height: var(--event-height);   /* JS calcula las 4 por evento (solapes en columnas) */
    display: grid; align-content: start; gap: 2px; min-width: 0; min-height: 34px;
    border-left: 3px solid var(--calendar-event-color); border-radius: var(--r-sm); padding: 6px 8px;
    background: var(--calendar-event-bg); color: var(--calendar-event-color); overflow: hidden; pointer-events: auto;
    transition: box-shadow var(--t), transform var(--t);
  }
  .coffee-calendar-time-event:hover { box-shadow: var(--shadow-sm); transform: translateY(-1px); }
  .coffee-calendar-time-event strong,
  .coffee-calendar-time-event span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .coffee-calendar-time-event strong { font-size: var(--fs-body); font-weight: 700; }
  .coffee-calendar-time-event span { font-size: var(--fs-caption); font-weight: 650; }

  /* ── Panel de trabajo diario: vínculo a las tareas del día seleccionado ── */
  .coffee-calendar-task-panel {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    border: 1px solid var(--line); border-left: 4px solid var(--blue); border-radius: var(--r-sm);
    padding: 12px; background: var(--surface-2);
  }
  .coffee-calendar-task-panel > div { display: grid; gap: 4px; min-width: 0; }
  .coffee-calendar-task-panel span { color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }

  /* ── Detalle del día: panel de tareas + tarjetas de evento en 2 columnas ── */
  .coffee-calendar-events { display: grid; grid-template-columns: minmax(220px, .55fr) minmax(260px, .9fr); gap: 10px; }
  .coffee-calendar-selected-empty {
    display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 40px;
    border: 1px dashed var(--line-2); border-radius: var(--r-sm); padding: 8px 12px; color: var(--muted);
  }
  .coffee-calendar-selected-empty strong { color: var(--ink); font-size: var(--fs-body); font-weight: 800; }

  .coffee-calendar-event-card {
    display: grid; gap: 10px; border: 1px solid var(--line); border-left: 4px solid var(--calendar-event-color);
    border-radius: var(--r); padding: 12px; background: var(--surface-2);
  }
  .coffee-calendar-event-card:hover { border-color: var(--line-2); box-shadow: var(--shadow-xs); }
  .coffee-calendar-event-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; align-items: start; }
  .coffee-calendar-event-head strong { overflow: hidden; color: var(--ink); font-size: var(--fs-body-lg); font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
  .coffee-calendar-event-head span { margin-top: 4px; color: var(--muted); font-size: var(--fs-small); }
  .coffee-calendar-event-head b { border-radius: 999px; padding: 4px 8px; background: var(--calendar-event-bg); color: var(--calendar-event-color); font-size: var(--fs-body-lg); font-weight: 800; white-space: nowrap; }
  .coffee-calendar-event-card .coffee-event-status-row { display: flex; flex-wrap: wrap; gap: 6px; }   /* única consumidora en el contrato: hijo estructural de la tarjeta */
  .coffee-calendar-event-meta { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
  .coffee-calendar-event-meta div { min-width: 0; border-top: 1px solid var(--line-soft); padding-top: 8px; }
  .coffee-calendar-event-meta dt { color: var(--muted); font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; }
  .coffee-calendar-event-meta dd { overflow: hidden; margin: 2px 0 0; color: var(--ink-2); font-size: var(--fs-small); font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }
  .coffee-calendar-event-card p { margin: 0; color: var(--muted); font-size: var(--fs-small); line-height: 1.4; }
  .coffee-calendar-event-actions { display: flex; flex-wrap: wrap; gap: 6px; }

  /* ── Modal: .modal-grid ya da 3 columnas; aquí solo se ancla qué campos
     ocupan el ancho completo y se reordena .modal-actions (Eliminar a la
     izquierda, Cancelar/Guardar a la derecha del mismo flex-end). ──────── */
  .calendar-event-products,
  .calendar-event-notes { grid-column: 1 / -1; }
  .calendar-event-actions { align-items: center; }
  .calendar-event-actions .danger { margin-right: auto; }

  /* ── Responsive: 1180/980/760/620 de v1 consolidan a 680 (toolbar y
     resumen ya no dependen de un ancho — flex-wrap/oculto los resuelve) ─── */
  @media (max-width: 680px) {
    .coffee-calendar-section,
    .coffee-calendar-panel { min-height: auto; }
    .coffee-calendar-events { grid-template-columns: 1fr; }
    .coffee-calendar-head,
    .coffee-calendar-grid { gap: 4px; }
    .coffee-calendar-day,
    .coffee-calendar-day--blank { min-height: 92px; }
    .coffee-calendar-day { padding: 6px; gap: 4px; }
    .coffee-calendar-day-number { font-size: var(--fs-h3); }
    .coffee-calendar-day-top { display: block; }
    .coffee-calendar-day-top > span:last-child,
    .coffee-calendar-day > small { display: none; }
    .coffee-calendar-day.has-events .coffee-calendar-day-top > span:last-child {
      display: block; color: var(--green); font-size: var(--fs-caption); font-weight: 800; text-align: right;
    }
    .coffee-calendar-day-event { min-height: 22px; padding: 2px 4px; }
    .coffee-calendar-day-event small,
    .coffee-calendar-day-more,
    .coffee-calendar-task-chip { display: none; }   /* celda angosta: solo el título del evento */
    .coffee-calendar-event-meta,
    .coffee-calendar-event-head { grid-template-columns: 1fr; }
  }
}

/* líneas: ~215 de presupuesto 150 (mes + semana/3 días/día con línea de
   tiempo + tarjetas de detalle + modal, todo en una sola vista; sin
   componente que absorba la coloración por tipo de evento ni la grilla
   horaria — motivo análogo al de toolbar/feedback/login-boot en P2) */
/* ══ ajustes — #view-settings (data-view="settings") ════════════════════════
   Cubre: .settings-section (respiro entre grupos), .settings-ios/-group-
     title/-group-note, .ios-list/.ios-row(--nav, .is-on)/-main/-controls/
     -chevron, .ios-inline-select, .auto-import-run-failed/-cron(.ok/.bad)/
     -hint, .settings-kpi-row (solo grilla — ver gap), .settings-subfamily-
     picker/-check(.is-included), .mom-rules-list/.mom-rule-row(.is-paused)/
     -head(-actions)/-name/-mini/-check/-sentence/-when/-values/-pattern/
     -chips, .mom-source-preview/.mom-preview-rows/-row/-label,
     .settings-logo-row/-preview (+ su .brand-mark anidado, tamaño propio
     del modal).
   NO se tocan aquí (ya resueltos arriba en la cascada): chrome de modal
     completo (.modal-backdrop/-panel, .settings-modal/--wide/-toolbar/
     -footer → modal.css/toolbar.css), .settings-area-tabs (toolbar.css, ya
     sin scopear — aplica igual en Revisión/Reglas/Ajustes), .settings-logo-
     picker completo, con su ícono y tipografía (dropzone.css, misma familia
     que .bank-file-picker — ver hallazgo abajo), .settings-vocabulary-grid/
     .mom-fallback-field (campo.css), .mom-rule-chip/.mom-rule-gastro (la
     píldora en sí)/.mom-preview-badge (badge.css), .table-action/.primary-
     action/.icon-button (boton.css), .empty/.inv-error (feedback.css).
   Fuente v1: styles.css ~L11182-11439 (bloque SETTINGS: kpi-row, logo-row/
     -preview/-picker, subfamily-picker/-check — el resto de ese bloque,
     .settings-grid/-panel/-panel-head/-brand-panel, quedó SIN EMISOR: la
     rediseñó a layout iOS y no se porta), ~L13708-14005 (vocabulario + mom-
     rule-* + mom-preview-*), ~L17936-18022 (ios-list/-row + auto-import),
     ~L18096-18098 (responsive 640), ~L18292-18349 (settings-area-tabs +
     settings-ios + chevron) — fusión last-wins.
   Casos límite: fila iOS sin control activo → .is-on ausente atenúa el texto
     (opacity); auto-import con último run fallido → mensaje en rojo (clase ya
     puesta por JS); subfamilia/insumo sin datos → picker cae en
     `.empty` (feedback, fuera de aquí); regla MoM pausada → tarjeta con borde
     punteado y atenuada, sigue editable; patrón regex activo → oculta el
     bloque de chips (JS con [hidden], base); grupo de ingreso sin nombre →
     placeholder del propio input (base); preview de clasificación vacío →
     JS no monta filas, `.mom-preview-rows` queda con :empty (sin regla propia,
     no la necesita: el borde de un contenedor vacío es aceptable).
   Nota de propiedad (mismo criterio que 05-vistas/inventario.css y
     cierre-de-turno.css): .ios-list/.ios-row(-main/-controls/-chevron),
     .settings-ios(-group-title/-group-note), .settings-subfamily-check,
     .mom-rule-row(-head/-name/-mini/-check/-sentence/-when/-values/-pattern),
     .mom-preview-rows/-row/-label y .settings-logo-preview no son instancia
     de ningún componente de 04-componentes — verificado por grep de cada
     clase contra los CSS de 04-componentes, 02-base.css, 03-layout.css y
     06-utilidades.css (así se detectaron los dos aciertos de abajo, que por
     eso NO se repiten aquí) — y no se reutilizan fuera de Ajustes (verificado
     por grep en index.php/app.js) — fijan también su identidad mínima
     (borde, superficie, tipografía con tokens), no solo su acomodo.
   Hallazgos de ese grep (nada que hacer aquí, ya resueltos arriba):
     `.settings-area-tabs` SÍ tiene regla propia (toolbar.css:281, sin
     scopear — misma línea sirve a Revisión/Reglas/Ajustes) y `.settings-
     logo-picker` SÍ está completo en dropzone.css: el primer borrador de
     este archivo los reescribía sin saberlo (re-skin no intencional de un
     componente ya resuelto) — se retiraron de aquí.
   NO cubre — gaps fuera de esta vista (no se resuelven aquí):
     · `.ui-switch` (el switch en sí — pista+perilla) se reutiliza en Mente
       (#view-mente, aún sin portar) además de en esta vista (drag del menú,
       filas de auto-import): falta nombrarlo en campo.css junto a
       `.check-line--switch` (misma receta, otro selector) — no se resuelve
       aquí por no ser exclusivo de Ajustes.
     · `.mom-rule-gastro input` (el switch dentro de la píldora food-cost):
       badge.css ya documenta "el switch input es de campo.css", pero esa
       regla no existe hoy en campo.css (verificado) — falta agregarla junto
       al gap de `.ui-switch` de arriba; hoy renderiza como checkbox nativo.
     · `.settings-kpi-row > article` (la tarjeta en sí: el markup
       article>small+strong+span calza exacto con la receta de kpi.css) no
       está en su lista `:is(...)`: candidata a sumarse ahí igual que
       `.annex-kpi-grid`. Esta vista solo fija la grilla de 2 columnas
       (decisión propia: kpi.css usa auto-fit, este modal quiere 2 fijas).
     · `.settings-actions` se reutiliza en el editor de catálogo de Eventos
       (fuera de Ajustes): no se resuelve aquí, sigue sin regla propia (los
       botones .table-action que contiene ya fluyen en línea sin ella).
   Cambios visibles documentados: pesos/tamaños fuera de escala → token más
     cercano (10.5px/780 → --fs-caption/800; 13.5px → --fs-body; 12.5px →
     --fs-body, mismo criterio que campo.css con .table-inline-field; 19px del
     chevron → --fs-h3; 760/620 de peso → 800/650, el peldaño más cercano;
     radio 16px del logo grande → --r-lg); espaciados fuera de {2,4,6,8,10,12,
     14,16,20,24,28,32,40,48} → peldaño más cercano (22→24, 13→12, 3→4, 30→32,
     9→8; el nudge de 1px del checkbox de subfamilia se retira, no hace falta
     con align-items:flex-start); borde "incluido" de `.settings-subfamily-
     check` (rgba crudo en v1) → fórmula canon de tokens.css (borde 26% sobre
     --green).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Estructura: pila de grupos con su propio respiro ─────────────────── */
  .settings-section { display: grid; gap: 14px; }
  .settings-ios { display: grid; gap: 24px; max-width: 980px; }   /* v1: 22px, fuera de escala */

  .settings-ios-group-title {
    margin: 0 0 8px 2px;
    color: var(--muted);
    font-size: var(--fs-caption); font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  }
  .settings-ios-group-note { margin: 10px 2px 0; color: var(--muted); font-size: var(--fs-small); line-height: 1.45; }

  /* ── Filas estilo iOS: lista de tarjeta única con separadores internos ── */
  .ios-list { display: grid; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); overflow: hidden; }
  .ios-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 12px 14px; }
  .ios-row + .ios-row { border-top: 1px solid var(--line); }
  .ios-list button.ios-row { width: 100%; margin: 0; border: 0; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; }
  .ios-row--nav:hover { background: color-mix(in srgb, currentColor 4%, transparent); }

  .ios-row-main { min-width: 0; display: grid; gap: 4px; }
  .ios-row-main strong { color: var(--ink); font-size: var(--fs-body); }
  .ios-row-main small { color: var(--muted); font-size: var(--fs-small); line-height: 1.4; }
  .ios-row:not(.is-on) .ios-row-main { opacity: .55; }   /* control apagado: texto atenuado, sigue legible */

  .ios-row-controls { display: flex; align-items: center; gap: 12px; flex: none; }
  .ios-inline-select { width: auto; min-height: var(--control-h-compact); padding: 4px 32px 4px 10px; font-size: var(--fs-body); font-weight: 650; }
  .ios-row-chevron { flex: none; color: var(--muted); font-size: var(--fs-h3); font-weight: 700; line-height: 1; }

  /* ── Color del menú: muestras, no desplegable ───────────────────────────
     Un <select> con seis nombres obliga a abrirlo y a imaginarse el color.
     Cada muestra pinta su propio tono con el degradado del riel (--nav-2
     arriba, --nav abajo: el mismo velo que lleva el riel de verdad), así que
     lo que se elige es lo que se ve. La seleccionada lleva anillo, no
     borde: engordar el borde movería el círculo un pixel. */
  .tono-muestras { display: flex; gap: 8px; }
  .tono-muestra {
    width: 26px;
    height: 26px;
    padding: 0;
    border: 1px solid var(--line-2);
    border-radius: 50%;
    cursor: pointer;
    transition: box-shadow var(--t), transform var(--t);
  }
  .tono-muestra:hover { transform: scale(1.08); }
  .tono-muestra[aria-checked="true"] {
    box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
  }
  .tono-muestra[data-tono="marino"]   { background: linear-gradient(160deg, var(--tono-marino-2), var(--tono-marino)); }
  .tono-muestra[data-tono="grafito"]  { background: linear-gradient(160deg, var(--tono-grafito-2), var(--tono-grafito)); }
  .tono-muestra[data-tono="petroleo"] { background: linear-gradient(160deg, var(--tono-petroleo-2), var(--tono-petroleo)); }
  .tono-muestra[data-tono="bosque"]   { background: linear-gradient(160deg, var(--tono-bosque-2), var(--tono-bosque)); }
  .tono-muestra[data-tono="vino"]     { background: linear-gradient(160deg, var(--tono-vino-2), var(--tono-vino)); }
  .tono-muestra[data-tono="cafe"]     { background: linear-gradient(160deg, var(--tono-cafe-2), var(--tono-cafe)); }

  /* ── Automatización: estado del cron + aviso de fallo ──────────────────── */
  .auto-import-run-failed { color: var(--red); font-weight: 700; }
  .auto-import-cron { font-weight: 800; }   /* v1: 760, fuera de escala */
  .auto-import-cron.ok { color: var(--green); }
  .auto-import-cron.bad { color: var(--red); }
  .auto-import-hint { margin: 10px 2px 0; font-size: var(--fs-small); }
  .auto-import-hint code { padding: 2px 6px; border: 1px solid var(--line); border-radius: var(--r-xs); background: var(--surface-2); font-size: var(--fs-caption); }

  /* ── Modal Food cost: KPIs (grilla 2×2 fija — no el auto-fit de kpi.css,
     ver gap arriba) + picker de subfamilias con scroll propio ───────────── */
  .settings-kpi-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin-top: 12px; }

  .settings-subfamily-picker {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 8px;
    max-height: min(58vh, 520px); overflow: auto; margin-top: 14px; padding-right: 4px;
  }
  .settings-subfamily-check {
    display: flex; align-items: flex-start; min-width: 0; gap: 8px; padding: 8px 10px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); color: var(--ink-2);
    cursor: pointer; transition: background var(--t), border-color var(--t), box-shadow var(--t);
  }
  .settings-subfamily-check.is-included {
    border-color: color-mix(in srgb, var(--green) 26%, transparent);
    background: var(--green-soft);
    box-shadow: inset 3px 0 0 var(--green);
  }
  .settings-subfamily-check input { flex: none; width: 15px; height: 15px; accent-color: var(--green); }
  .settings-subfamily-check strong { overflow: hidden; color: var(--ink); font-size: var(--fs-body); line-height: 1.18; text-overflow: ellipsis; white-space: nowrap; }
  .settings-subfamily-check small { display: block; margin-top: 4px; color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }

  /* ── Modal Grupos de ingreso: cada regla es una tarjeta-oración ─────────
     "Cuando [campo] [condición] [valores], destino [pill food-cost]". ───── */
  .mom-rules-list { display: grid; gap: 10px; }
  .mom-rule-row {
    display: grid; gap: 12px; padding: 14px 16px;
    border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2);
    transition: opacity var(--t), border-color var(--t);
  }
  .mom-rule-row.is-paused { opacity: .55; border-style: dashed; }

  .mom-rule-head { display: flex; align-items: center; gap: 12px; }
  .mom-rule-name { flex: 1 1 auto; min-width: 0; font-size: var(--fs-body-lg); font-weight: 700; }
  .mom-rule-head-actions { display: flex; align-items: center; gap: 12px; flex: none; }
  .mom-rule-mini { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: var(--fs-caption); font-weight: 650; white-space: nowrap; }
  .mom-rule-mini input { width: 56px; padding: 4px 6px; font-size: var(--fs-small); text-align: right; }
  .mom-rule-check { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: var(--fs-small); font-weight: 650; cursor: pointer; white-space: nowrap; }

  .mom-rule-sentence { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
  .mom-rule-when { color: var(--muted); font-size: var(--fs-small); font-weight: 650; }
  .mom-rule-sentence select { flex: none; width: auto; height: var(--control-h-compact); font-size: var(--fs-body); font-weight: 650; }
  .mom-rule-values { flex: 1 1 260px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
  .mom-rule-values [data-rule-value-input] { flex: 1 1 180px; min-width: 150px; height: var(--control-h-compact); font-size: var(--fs-body); }
  .mom-rule-pattern { flex: 1 1 260px; min-width: 0; height: var(--control-h-compact); font-size: var(--fs-body); font-family: ui-monospace, "SF Mono", Menlo, monospace; }
  .mom-rule-chips { display: contents; }   /* los chips fluyen como hijos directos de .mom-rule-values */

  /* ── Preview de clasificación (último mes): tabla ligera de 4 columnas ── */
  .mom-source-preview { margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--line); }
  .mom-preview-rows { display: grid; margin-top: 8px; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
  .mom-preview-row {
    display: grid; grid-template-columns: minmax(140px, 1.4fr) auto 1fr 64px; align-items: center; gap: 12px;
    padding: 8px 14px; background: var(--surface); font-size: var(--fs-body);
  }
  .mom-preview-row + .mom-preview-row { border-top: 1px solid var(--line-soft); }
  .mom-preview-label { font-weight: 650; }
  .mom-preview-row strong,
  .mom-preview-row .muted { text-align: right; font-variant-numeric: tabular-nums; }

  /* ── Modal Logo: vista previa (marca actual) + selector de archivo ──────
     Preview a la izquierda algo más ancho que el picker (proporción propia
     del modal, no el 50/50 genérico). ─────────────────────────────────── */
  .settings-logo-row { display: grid; grid-template-columns: minmax(260px, 1fr) minmax(220px, .85fr); gap: 12px; align-items: stretch; }

  .settings-logo-preview {
    min-width: 0; display: flex; align-items: center; gap: 12px; padding: 12px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2);
  }
  .settings-logo-preview .brand-mark { width: 58px; height: 58px; border-radius: var(--r-lg); padding: 8px; }   /* v1: 16px de radio → --r-lg (13px) */
  .settings-logo-preview strong { display: block; color: var(--ink); font-size: var(--fs-body-lg); line-height: 1.2; }
  .settings-logo-preview small { display: block; overflow: hidden; margin-top: 4px; color: var(--muted); font-size: var(--fs-small); font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }

  /* .settings-logo-picker (columna derecha): completo en 04-componentes/
     dropzone.css ("COMPACTA construida: settings-logo-picker") — misma
     familia que .bank-file-picker; no se re-skinea aquí. */

  /* ── Responsive (v1: 920/640 ad-hoc → 900/680 canon) ────────────────────── */
  @media (max-width: 900px) {
    .settings-logo-row { grid-template-columns: 1fr; }
    .mom-rule-head { flex-wrap: wrap; }
    .mom-preview-row { grid-template-columns: 1fr auto; }
  }

  @media (max-width: 680px) {
    .ios-row { flex-wrap: wrap; }
    .ios-row-controls { width: 100%; justify-content: space-between; }
  }
}

/* líneas: ~210 de presupuesto 150 — excedido y documentado (mismo criterio
   que inventario.css/eventos.css): Ajustes concentra 5 sub-sistemas propios
   sin dueño en 04-componentes (lista iOS, food cost, grupos de ingreso con
   sus tarjetas-oración, preview de clasificación, logo) que no se reusan
   fuera de esta vista — ver gaps documentados arriba para lo que sí cruza. */
/* ══ aportes-retiros — data-view="owner-account" ════════════════════════════
   Cubre: #view-owner-account .section-head (fusión .flow-head +
     .financial-operations-head sobre la misma cabecera), .owner-account-panel-head
     (alineación contra .finance-panel-head base), .accounting-table-wrap +
     .owner-account-table (ancho mínimo, alineación de columnas de cifra,
     columna de acción sin wrap).
   Fuente v1: styles.css ~L15836 (.financial-operations-head), ~L9372
     (.flow-head — ambas conviven en el section-head de esta vista, igual
     que en Cuentas por pagar), ~L16016 (.owner-account-panel-head, solo el
     align-items difiere del finance-panel-head base) + ~L16137 (su eco en
     760px), ~L15930-15937 (.accounting-table-wrap/-table genéricas),
     ~L16037-16041 (owner-account-table: alineación de columna 3 y última +
     reclassify sin wrap, fusionadas aquí) — fusión last-wins.
   Casos límite: sin movimientos del periodo → fila única con colspan que
     pinta app.js sobre .empty (ya vestida en 02-base, sin regla aquí);
     "Movimiento" es texto libre sin truncar (v1 no lo truncaba: la fila
     crece); saldo en borrador → pill "Borrador" en vez de cifra
     (.finance-status-badge, ya resuelto en badge.css); movimiento sin
     origen bancario → texto "No bancario" en vez de botón "Reclasificar",
     misma celda angosta.
   Nota — alineación: v1 forzaba la columna 3 (Asiento/fuente) a la
     izquierda de forma explícita, pero esa columna nunca cae dentro de las
     últimas 4 de esta tabla de 7 — ya nace alineada a la izquierda por la
     receta base de <td>. Esa regla no se porta (deuda muerta, no intención
     visible). Las 4 últimas sí llevan cifra o acción-espejo-de-cifra
     (Retiros/Aportes/Saldo/Acción) y se alinean con nth-child(n+4), la
     misma forma corregida que usa Cuentas por pagar.
   NO cubre — gap de componente, no de esta vista (mismo criterio que
     cuentas-por-pagar.css y cuentas-por-cobrar.css, verificado sin regla en
     04-componentes hoy, 2026-08-09):
     · `.owner-account-reading` — banner de lectura contable (acento lateral
       de color por tono debit/credit/zero + cifra protagonista a la
       derecha); sin familia clara en feedback.css hoy.
     · `.owner-account-filters` — grupo de pills de filtro con contador;
       distinto del `.segmented-control` de toolbar.css (aquí cada opción
       es su propio botón-pill, no una fila con indicador deslizante).
     · `.owner-account-footnote`, `.owner-account-no-action` — nota al pie y
       texto meta atenuado, sin dueño hoy.
     · `.owner-account-table tr.is-draft` — mismo hueco de fila-borrador que
       Libro mayor (`renderAccountingLedger` comparte la misma clase); se
       resuelve donde se resuelva esa tabla hermana, no aquí.
     · `.accounting-kpi-grid`/`.accounting-panel` — gap ya documentado en
       cuentas-por-pagar.css; `.owner-account-kpis` es solo un hook (v1
       nunca le dio regla propia, viaja siempre junto a `.accounting-kpi-grid`).
     `.finance-period-tools` tampoco: la resuelve el combo
     `.book-tools` + `.financial-close-month-picker` + `.icon-button` sin
     ajuste (mismo combo que Resumen financiero y Cuentas por pagar).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Título arriba, selector de periodo abajo a la derecha: misma fusión
     .flow-head + .financial-operations-head que Cuentas por pagar. */
  #view-owner-account .section-head {
    align-items: flex-end;
    margin-bottom: 14px;
  }

  /* Cabecera del mayor auxiliar: título a la izquierda, botón "Abrir en
     Libro mayor" centrado contra él (finance-panel-head base alinea arriba,
     pensado para cifras que crecen hacia abajo, no para esta fila baja). */
  #view-owner-account .owner-account-panel-head { align-items: center; }

  /* 7 columnas con dos de texto libre (Movimiento, Asiento/fuente): sin
     ancho mínimo el navegador las aprieta antes de ofrecer scroll
     horizontal dentro de .accounting-table-wrap. */
  #view-owner-account .accounting-table-wrap { overflow-x: auto; }
  #view-owner-account .owner-account-table { min-width: 940px; }

  /* Las 4 últimas columnas son cifra o su acción-espejo (Retiros / Aportes /
     Saldo / Acción); la acción no envuelve — "Reclasificar" y "No bancario"
     comparten la misma celda angosta que las cifras de al lado. */
  #view-owner-account .owner-account-table th:nth-child(n+4),
  #view-owner-account .owner-account-table td:nth-child(n+4) {
    text-align: right;
  }
  #view-owner-account .owner-account-table td:last-child { white-space: nowrap; }

  @media (max-width: 680px) {
    /* Vuelve al top-align base: el botón "Abrir en Libro mayor" pasa a su
       propia línea bajo el título en vez de flotar centrado contra él. */
    #view-owner-account .owner-account-panel-head { align-items: flex-start; }
  }

  /* Acción de reclasificar: en una celda estrecha, el rótulo no debe partirse
     en dos líneas y desalinear la fila. */
  #view-owner-account .owner-account-reclassify { white-space: nowrap; }
}

/* líneas: 92 de presupuesto 150 */
/* ══ balance (#view-balance-sheet, data-view="balance-sheet") ══════════════
   Cubre: #view-balance-sheet .section-head (solo alineación), .balance-sheet-
     layout, .balance-sheet-section, .balance-sheet-section-head,
     .balance-sheet-row(.is-result), .balance-sheet-total,
     .balance-sheet-section .empty (padding).
   Fuente v1: styles.css ~L15836 (.financial-operations-head align-items),
     ~L9372 (.flow-head margin-bottom — ambas conviven en el mismo
     .section-head, igual que en cuentas-por-pagar.css), ~L15977-15992
     (familia .balance-sheet-*), ~L16126-16134 (responsive 760px, consolidado
     a 680px) — fusionadas last-wins.
   Casos límite: fila con saldo bajo umbral (< $0,01 y sin actividad) ya la
     filtra el JS antes del DOM (financialPositionSectionMarkup) — no hay
     "fila vacía", solo sección entera sin filas → .balance-sheet-section
     .empty ("Sin saldo"); cifra negativa → .is-negative; fila "virtual"
     (resultado del ejercicio, calculado) llega con el botón disabled y sin
     código de cuenta visible; nombre de cuenta largo → v1 no truncaba (el
     botón es flex/baseline sin ellipsis), se preserva: envuelve en vez de
     cortar; sección sin total (Activos/pasivos corrientes) simplemente no
     pinta .balance-sheet-total, sin regla especial.
   Nota de propiedad: .balance-sheet-section/-section-head/-row/-total no
     son instancia de ningún componente de 04-componentes (verificado en los
     "Cubre" de panel/tabla/badge) — son el layout propio del estado de
     situación financiera, sin dueño arriba en la cascada. Por eso esta vista
     fija también su identidad mínima (borde, tinte, tipografía) con tokens,
     mismo criterio que 05-vistas/cierre-de-turno.css con la celda de día.
     8.5px (código de cuenta) y 780/730 de peso no existen en la escala
     cerrada: se suben al piso 11px (--fs-caption) y al peso permitido más
     cercano (800/700) — cambio visible menor, documentado aquí.
   NO cubre — gap de componente compartido, no de esta vista (ya documentado
     en NOTAS-P2.md #11, que ya lista "Situación financiera" entre las vistas
     afectadas): .accounting-kpi-grid + su modificador .balance-sheet-kpis
     (franja KPI superior) y .accounting-basis-banner (banner de periodo,
     pintado por financeBasisBanner() — también usado por Cuentas por pagar/
     cobrar, Libros y Cuenta del propietario). No se resuelve por decreto de
     esta vista; candidatos siguen siendo kpi.css y feedback.css.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Título arriba, selector de período abajo a la derecha: misma fusión
     .financial-operations-head + .flow-head sobre .section-head que
     Cuentas por pagar (el .section-head base de panel.css centra vertical). */
  #view-balance-sheet .section-head {
    align-items: flex-end;
    margin-bottom: 14px;
  }

  /* ── Dos columnas: Activos | Pasivos y patrimonio ──
     Cada columna apila sus secciones (corriente + no corriente/patrimonio)
     con el mismo ritmo vertical; sin margin-top propio porque el banner y
     la franja KPI que la preceden ya traen su propio margin-bottom (familia
     accounting-*, ver NO cubre). Gap 20px (v1: 18, fuera de escala). */
  #view-balance-sheet .balance-sheet-layout {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 20px;
    align-items: start;
  }
  #view-balance-sheet .balance-sheet-layout > div {
    display: grid;
    gap: 14px;
  }

  /* ── Tarjeta de sección contable (widget propio, ver Nota de propiedad) ── */
  #view-balance-sheet .balance-sheet-section {
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
  }
  #view-balance-sheet .balance-sheet-section .empty { padding: 10px 16px; }

  /* Cabecera de sección y pie de total comparten la misma banda.
     Padding 12px 16px (v1: 13/15, fuera de escala). */
  #view-balance-sheet .balance-sheet-section-head,
  #view-balance-sheet .balance-sheet-total {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 12px 16px;
    background: var(--surface-2);
  }
  #view-balance-sheet .balance-sheet-section-head h3 {
    margin: 0;
    color: var(--ink);
    font-size: var(--fs-body-lg);
  }
  #view-balance-sheet .balance-sheet-section-head strong,
  #view-balance-sheet .balance-sheet-total strong {
    color: var(--ink);
    font-size: var(--fs-body-lg);
    font-variant-numeric: tabular-nums;
  }
  #view-balance-sheet .balance-sheet-total {
    border-top: 1px solid var(--line-strong);
    font-weight: 800;
  }

  /* Fila de cuenta: nombre+código a la izquierda (envuelve, no trunca — ver
     Casos límite), cifra siempre alineada a la derecha. */
  #view-balance-sheet .balance-sheet-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    border-top: 1px solid var(--line);
  }
  #view-balance-sheet .balance-sheet-row button {
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--ink-2);
    text-align: left;
    cursor: pointer;
  }
  #view-balance-sheet .balance-sheet-row button:disabled { cursor: default; }
  #view-balance-sheet .balance-sheet-row button small {
    flex: none;
    color: var(--blue);
    font-size: var(--fs-caption);
  }
  #view-balance-sheet .balance-sheet-row > strong {
    color: var(--ink-2);
    font-size: var(--fs-caption);
    font-variant-numeric: tabular-nums;
  }
  #view-balance-sheet .balance-sheet-row > strong.is-negative { color: var(--red); }

  /* Fila "virtual" (resultado del ejercicio, calculado, sin botón activo):
     tinte sutil de marca — separa el subtotal de las cuentas reales. */
  #view-balance-sheet .balance-sheet-row.is-result {
    background: color-mix(in srgb, var(--brand) 4%, var(--surface));
    font-weight: 700;
  }

  @media (max-width: 680px) {
    #view-balance-sheet .balance-sheet-layout { grid-template-columns: 1fr; }
  }
}

/* líneas: ~150 de presupuesto 150 — al techo: 4 de las clases (balance-
   sheet-section/-section-head/-row/-total) no tienen componente dueño, así
   que esta vista es también su única identidad visual, no solo su
   geometría (mismo criterio que cierre-de-turno.css con la celda de día). */
/* ══ cartolas-bancos — data-view="bank-statements" ══════════════════════════
   Cubre: #view-bank-statements .financial-operations-head (scoped: la
     comparte con Activo Fijo, ver Fuente); .financial-operation-grid/-actions
     (idem, scoped); .bank-source-drawer(-body/-column), .bank-batch-grid,
     .bank-batch-account(-head/-body), .bank-batch-empty,
     .mp-step-list/.mp-step-body, .mp-statement-upload-form, .mp-capture-stats,
     .mp-source-blocker(.is-*), .mp-source-results, .mp-ambiguity-group-grid,
     .bank-batch-review-bar, .bank-account-columns/-group, .bank-statement-list,
     .bank-statement-card-head, .bank-balance-strip, .bank-statement-movement-
     body, .bank-movement-list-head/-row-main/-editor(-primary/-support/-meta),
     .bank-document-option, .bank-evidence-control, .bank-derived-debt-body.
   Fuente v1: styles.css ~L16150-18500 — la sección tiene 3 capas superpuestas
     (retícula de tarjetas con icono/acento por fuente → rediseño intermedio
     con @container 1080 → "checklist plano" final que aplana el anidado a
     drawer+tarjeta). Se porta la INTENCIÓN de la última capa (last-wins),
     no sus tres versiones; fusionadas con @media 1500/1180/900 dispersos.
   Nota de propiedad: ninguna clase "bank-" o "mp-" aparece en otra vista
     (verificado con grep en app.js: 100% de las apariciones caen dentro de
     renderBankStatements/mercadoPagoCaptureMarkup/mercadoPagoSourceStatus-
     Markup) y ninguna está en los 17 componentes núcleo — esta vista es
     también su única definición de identidad visual (superficie), no sólo
     su geometría, igual que 05-vistas/pedidos.css y tareas-diarias.css.
   NO se toca aquí (ya resuelto arriba, re-skinearlo estaría prohibido):
     .finance-status-badge/.bank-row-status (badge.css), .bank-movement-
     filter-bar (toolbar.css), .mp-capture-empty/-error (feedback.css),
     .account-help-* (popover.css), .is-positive/-negative (utilidades.css),
     .bank-file-picker/-control (dropzone.css), tamaño de botón compacto en
     .bank-batch-review-bar/.bank-source-drawer/.bank-movement-editor-primary
     (boton.css).
   Cambios visibles (deuda que no se porta): se retira el icono "B/BC/BI/LC/
     TC/MP" y el acento de color por tipo de fuente (banco azul/deuda índigo/
     MP ámbar → v1 mismo terminó unificándolos en un solo acento y los quitó
     del todo). Se conservan los círculos de estado, los chevrons propios y
     el aviso de traspaso de clasificaciones porque sí codifican jerarquía y
     estado operativo, aunque adoptan los tokens visuales de v2.
   Casos límite: cartola sin movimientos → .empty (feedback.css); glosa o
     contraparte larga → ellipsis en la fila, wrap normal dentro del editor
     abierto; cuenta sin cartola → .is-missing sin franja de saldos; período
     cerrado → overlay de aviso fuera de esta vista (.financial-blockers es
     compartido por 5+ vistas de Finanzas, no tiene dueño en 04-componentes:
     gap de componente, no se resuelve aquí).
   Decisión de fila de movimiento (el bug de origen de esta vista): en vez
     de una rejilla ancha con @container 1080 (v1) o breakpoints ad-hoc en
     1180/1500px que no coincidían con el ancho REAL de la tarjeta cuando
     Bancos/Deudas están lado a lado, la rejilla usa columnas angostas fijas
     + minmax fluido con un mínimo menor al piso de columna de
     .bank-account-columns (660px): cabe siempre sin cambiar de forma. Si un
     dato extremo (glosa sin espacios, zoom de texto) igual no cabe, el
     contenedor scrollea (overflow-x) — nunca se recorta.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Esta vista es una mesa de trabajo de dos columnas, no una página de
     lectura. Usa todo el ancho disponible para evitar comprimir saldos,
     estados y acciones dentro del límite editorial general. */
  #view-bank-statements { max-width: none; }

  /* ── Cabecera de vista: más compacta que el resto de Finanzas. ── */
  #view-bank-statements .financial-operations-head { margin-bottom: 12px; }

  /* ── Formularios de carga (compartidos con Activo Fijo): en esta vista
     sus dos hijos son siempre span-2 (una sola columna de facto). ── */
  #view-bank-statements .financial-operation-grid { display: grid; gap: 12px; }
  #view-bank-statements .financial-operation-actions {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  }
  #view-bank-statements .financial-operation-actions small { margin-right: auto; }

  /* ── Drawer de fuentes: Bancos+Deudas a la izquierda, Mercado Pago a la
     derecha; auto-fit con piso de 620px evita la "zona muerta" de v1 (nunca
     arma una columna más angosta que ese piso, así que no necesita
     @container ni un breakpoint aparte para decidirlo). ── */
  .bank-source-drawer {
    margin-bottom: 12px;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
    overflow: hidden;
  }
  .bank-source-drawer > summary {
    display: flex; align-items: center; justify-content: space-between;
    gap: 16px; min-height: 56px; padding: 10px 16px; cursor: pointer;
    transition: background var(--t);
  }
  .bank-source-drawer > summary:hover { background: var(--surface-2); }
  .bank-source-drawer > summary > div { min-width: 0; }
  .bank-source-drawer > summary :is(small, strong, div > span) { display: block; }
  .bank-source-drawer > summary small {
    color: var(--muted); font-size: var(--fs-caption); font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase;
  }
  .bank-source-drawer > summary strong { margin-top: 2px; font-size: var(--fs-body-lg); }
  .bank-source-drawer > summary div > span {
    margin-top: 2px; overflow: hidden; color: var(--muted);
    font-size: var(--fs-caption); text-overflow: ellipsis; white-space: nowrap;
  }
  .bank-source-drawer-action {
    position: relative; flex: 0 0 auto; padding: 7px 28px 7px 10px;
    border: 1px solid var(--line); border-radius: var(--r-sm);
    background: var(--surface-2); color: var(--ink-2);
    font-size: var(--fs-caption); font-weight: 700;
    transition: background var(--t), border-color var(--t), color var(--t);
  }
  .bank-source-drawer-action::after {
    position: absolute; top: 50%; right: 10px; width: 7px; height: 7px;
    border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
    content: ""; transform: translateY(-65%) rotate(45deg); transition: transform var(--t);
  }
  .bank-source-drawer[open] .bank-source-drawer-action::after { transform: translateY(-30%) rotate(225deg); }
  .bank-source-drawer-body {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(620px, 1fr));
    gap: 20px 24px;
    align-items: start;
    padding: 16px;
    border-top: 1px solid var(--line);
  }
  .bank-source-column { display: grid; gap: 16px; min-width: 0; }
  .source-group-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 14px; padding: 0 2px 8px;
  }
  .source-group-head > div { min-width: 0; }
  .source-group-head strong {
    display: block; color: var(--ink); font-size: var(--fs-caption);
    font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  }
  .source-group-head div > span { display: block; margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); line-height: 1.4; }
  .source-progress { display: block; width: min(300px, 100%); height: 4px; margin-top: 6px; overflow: hidden; border-radius: 999px; background: var(--line); }
  .source-progress i { display: block; height: 100%; border-radius: inherit; background: var(--green); transition: width var(--t); }
  .mp-source-workspace { display: grid; gap: 12px; min-width: 0; }

  /* Las tres fuentes comparten superficie; el tipo sólo se comunica en el
     rótulo y no vuelve a introducir la paleta múltiple de v1. */
  .bank-bank-upload-panel,
  .bank-operating-upload-panel,
  .bank-financing-upload-panel,
  .bank-source-drawer .mp-source-workspace,
  .bank-source-drawer .mp-capture-detail {
    min-width: 0; overflow: hidden; border: 1px solid var(--line);
    border-radius: var(--r); background: var(--surface);
  }
  .bank-bank-upload-panel + .bank-bank-upload-panel { margin-top: 12px; }
  .bank-source-panel-head,
  .mp-source-workspace-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 14px; padding: 10px 12px; border-bottom: 1px solid var(--line);
  }
  :is(.bank-source-panel-head, .mp-source-workspace-head) > div { min-width: 0; }
  :is(.bank-source-panel-head, .mp-source-workspace-head) :is(.eyebrow, h3, span) { display: block; }
  :is(.bank-source-panel-head, .mp-source-workspace-head) .eyebrow {
    color: var(--muted); font-size: var(--fs-caption); font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase;
  }
  :is(.bank-source-panel-head, .mp-source-workspace-head) h3 { margin: 2px 0; font-size: var(--fs-body-lg); }
  :is(.bank-source-panel-head, .mp-source-workspace-head) > div > span { color: var(--muted); font-size: var(--fs-caption); }
  .bank-bank-upload-panel .financial-operation-grid { gap: 9px; padding: 10px 12px; }
  .bank-bank-upload-panel .financial-operation-actions { padding: 0 12px 12px; }

  .bank-balance-field > :is(span, small) { display: block; }
  .bank-balance-field > span {
    margin-bottom: 4px; color: var(--muted); font-size: var(--fs-caption);
    font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  }
  .bank-balance-field > small { margin-top: 4px; color: var(--muted); font-size: var(--fs-caption); line-height: 1.35; }
  .bank-balance-field input[type="number"] { width: 100%; font-variant-numeric: tabular-nums; text-align: right; }

  /* ── Idioma compartido: lista con un solo borde, filas sin borde propio.
     Misma receta para las 3 familias de acordeón de la vista. ── */
  .bank-batch-grid,
  .mp-step-list,
  .bank-statement-list {
    display: block;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
    overflow: hidden;
  }
  .bank-batch-grid > .bank-batch-account,
  .bank-statement-list > .bank-statement-card { border: 0; }
  .bank-batch-grid > .bank-batch-account + .bank-batch-account,
  .mp-step-list > .mp-step + .mp-step,
  .bank-statement-list > .bank-statement-card + .bank-statement-card {
    border-top: 1px solid var(--line);
  }
  .bank-statement-list .empty { padding: 24px 16px; }

  /* ── Tarjeta de cuenta/paso: cabecera + cuerpo de dos columnas (adjunto |
     saldo inicial); la casilla "sin movimientos" ocupa el ancho completo. ── */
  .bank-batch-account-head {
    position: relative;
    display: flex; align-items: center; justify-content: space-between;
    gap: 14px; min-height: 54px; padding: 9px 40px 9px 46px;
  }
  .bank-batch-account > summary {
    cursor: pointer;
    list-style: none;
    transition: background var(--t);
  }
  .bank-batch-account > summary::-webkit-details-marker { display: none; }
  .bank-batch-account > summary:hover { background: var(--surface-2); }
  .bank-batch-account-head > div { min-width: 0; }
  .bank-batch-account-head strong,
  .bank-batch-account-head span {
    display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .bank-batch-account-head span { margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); }
  .bank-batch-account-head .finance-status-badge { flex: 0 0 auto; }
  .bank-batch-account-head > div::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 13px;
    width: 20px;
    height: 20px;
    border: 1px solid var(--line-2);
    border-radius: 50%;
    background: var(--surface);
    transform: translateY(-50%);
    transition: background var(--t), border-color var(--t);
  }
  .bank-batch-account.is-pending > .bank-batch-account-head > div::before { border-color: var(--amber); }
  .bank-batch-account.is-complete > .bank-batch-account-head > div::before {
    content: "✓";
    border-color: var(--green);
    background: var(--green);
    color: var(--surface);
    font-size: var(--fs-caption);
    font-weight: 800;
    line-height: 18px;
    text-align: center;
  }
  .bank-batch-account > summary::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 17px;
    width: 7px;
    height: 7px;
    border-right: 1px solid var(--muted);
    border-bottom: 1px solid var(--muted);
    transform: translateY(-62%) rotate(45deg);
    transition: transform var(--t);
  }
  .bank-batch-account[open] > summary::after { transform: translateY(-38%) rotate(225deg); }
  .bank-batch-account-body {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(200px, .6fr);
    gap: 12px;
    padding: 12px 16px 16px 46px;
  }
  .bank-batch-account[open] > summary { border-bottom: 1px solid var(--line); }
  .bank-batch-account-body .bank-batch-empty {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    color: var(--ink-2);
    font-size: var(--fs-caption);
    font-weight: 550;
    letter-spacing: 0;
    text-transform: none;
  }
  .bank-batch-empty input { flex: none; width: 16px; height: 16px; }
  .bank-batch-account-body label span em {
    margin-left: 4px;
    color: var(--muted);
    font-style: normal;
    font-weight: 550;
    text-transform: none;
  }
  .mp-step-body { display: grid; gap: 12px; padding: 12px 16px 16px 46px; }
  .mp-statement-upload-form {
    display: grid;
    grid-template-columns: minmax(0, 1.4fr) minmax(160px, .6fr) auto;
    align-items: end;
    gap: 10px;
  }
  .mp-statement-upload-form label { min-width: 0; }
  .mp-statement-upload-form label > :is(span, small) { display: block; }
  .mp-statement-upload-form label > span { margin-bottom: 4px; }
  .mp-statement-upload-form label > small {
    margin-top: 4px;
    overflow: hidden;
    color: var(--muted);
    font-size: var(--fs-caption);
    line-height: 1.35;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .mp-statement-upload-form > button { align-self: end; }

  /* ── Mercado Pago: aviso de estado del cruce, estadísticas de captura y
     resultado por categoría (coinciden/sólo cartola/sólo captura/ambiguos). ── */
  .mp-source-blocker {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-2);
  }
  .mp-source-blocker :is(strong, span) { display: block; }
  .mp-source-blocker strong { flex: none; font-size: var(--fs-caption); }
  .mp-source-blocker span { color: var(--muted); font-size: var(--fs-caption); line-height: 1.4; text-align: right; }
  .mp-source-blocker.is-green {
    border-color: color-mix(in srgb, var(--green) 30%, var(--line));
    background: var(--green-soft);
  }
  .mp-source-blocker.is-amber {
    border-color: color-mix(in srgb, var(--amber) 42%, var(--line));
    background: var(--amber-soft);
  }
  .mp-capture-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
  .mp-capture-stats span { color: var(--muted); font-size: var(--fs-caption); line-height: 1.35; }
  .mp-capture-stats strong { display: block; color: var(--ink); font-size: var(--fs-body); }
  .mp-source-results { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
  .mp-source-results article { min-width: 0; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); }
  .mp-source-results :is(small, strong, span) { display: block; }
  .mp-source-results small {
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
  }
  .mp-source-results strong { margin-top: 2px; font-size: var(--fs-body-lg); }
  .mp-source-results span { margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); line-height: 1.35; }
  .mp-source-results .is-matched { border-color: color-mix(in srgb, var(--green) 30%, var(--line)); }
  .mp-source-results .is-matched strong { color: var(--green); }
  .mp-source-results .is-statement { border-color: color-mix(in srgb, var(--blue) 28%, var(--line)); }
  .mp-source-results .is-capture { border-color: color-mix(in srgb, var(--amber) 28%, var(--line)); }
  .mp-source-results .is-ambiguous {
    border-color: color-mix(in srgb, var(--red) 32%, var(--line));
    background: var(--red-soft);
  }
  .mp-ambiguity-group-grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(240px, .85fr); gap: 10px; }

  /* Revisión de cruces ambiguos. */
  .mp-ambiguity-review { padding: 10px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .mp-ambiguity-review-head,
  .mp-ambiguity-row-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
  .mp-ambiguity-review-head { margin-bottom: 8px; }
  .mp-ambiguity-review-head span,
  .mp-ambiguity-row-head small { color: var(--muted); font-size: var(--fs-caption); }
  .mp-ambiguity-group { margin-top: 8px; overflow: hidden; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
  .mp-ambiguity-group > summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 12px; cursor: pointer; list-style: none; }
  .mp-ambiguity-group > summary::-webkit-details-marker { display: none; }
  .mp-ambiguity-group[open] > summary { border-bottom: 1px solid var(--line); }
  .mp-ambiguity-group > summary :is(strong, small) { display: block; }
  .mp-ambiguity-group > summary small,
  .mp-ambiguity-group > summary > span:last-child { color: var(--muted); font-size: var(--fs-caption); }
  .mp-ambiguity-group-grid h5 { margin: 0 0 6px; color: var(--muted); font-size: var(--fs-caption); letter-spacing: .06em; text-transform: uppercase; }
  .mp-ambiguity-statement { padding: 10px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); }
  .mp-ambiguity-statement + .mp-ambiguity-statement { margin-top: 8px; }
  .mp-ambiguity-row-head :is(small, strong) { display: block; }
  .mp-ambiguity-row-head strong { margin-top: 2px; font-size: var(--fs-caption); }
  .mp-ambiguity-row-head b { white-space: nowrap; font-size: var(--fs-caption); }
  .mp-ambiguity-statement > p { margin: 4px 0 8px; color: var(--muted); font-size: var(--fs-caption); }
  .mp-ambiguity-statement label > span { display: block; margin-bottom: 4px; color: var(--muted); font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; }
  .mp-ambiguity-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
  .mp-ambiguity-candidates { display: grid; gap: 6px; }
  .mp-ambiguity-candidate {
    display: grid; grid-template-columns: 22px minmax(0, 1fr) auto;
    gap: 8px; align-items: center; padding: 8px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
  }
  .mp-ambiguity-candidate > span { display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: var(--ink); color: var(--surface); font-size: var(--fs-caption); font-weight: 800; }
  .mp-ambiguity-candidate :is(strong, small) { display: block; }
  .mp-ambiguity-candidate small { margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); }
  .mp-ambiguity-candidate b { white-space: nowrap; font-size: var(--fs-caption); }
  .mp-ambiguity-remaining { margin: 8px 0 0; color: var(--muted); font-size: var(--fs-caption); text-align: center; }

  /* Captura local de Mercado Pago. */
  .mp-capture-section { display: grid; gap: 8px; margin-bottom: 12px; }
  .mp-source-step-number { display: grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: var(--ink); color: var(--surface); font-size: var(--fs-caption); font-weight: 800; }
  .mp-source-policy { margin: 0; padding: 8px 12px; border-top: 1px solid var(--line); color: var(--muted); font-size: var(--fs-caption); line-height: 1.5; }
  .mp-source-policy summary { color: var(--ink-2); font-weight: 700; cursor: pointer; }
  .mp-source-policy p { margin: 6px 0 0; }

  .mp-capture-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, .8fr); gap: 12px; padding: 12px 16px 16px; border-top: 1px solid var(--line); }
  .mp-capture-install,
  .mp-capture-session { padding: 12px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
  .mp-capture-install { display: flex; align-items: center; gap: 10px; background: var(--surface-2); }
  .mp-capture-install > span { min-width: 0; flex: 1; }
  .mp-capture-install :is(small, strong, code) { display: block; }
  .mp-capture-install small { color: var(--muted); font-size: var(--fs-caption); letter-spacing: .07em; text-transform: uppercase; }
  .mp-capture-install code { margin-top: 4px; overflow-wrap: anywhere; color: var(--muted); font-size: var(--fs-caption); }
  .mp-capture-steps { margin: 10px 0 0; padding-left: 22px; color: var(--muted); font-size: var(--fs-caption); line-height: 1.55; }
  .mp-capture-help { border-top: 1px solid var(--line); }
  .mp-capture-help > summary { padding-top: 8px; color: var(--ink-2); font-size: var(--fs-caption); font-weight: 700; cursor: pointer; }
  .mp-capture-help .mp-capture-install { margin-top: 8px; }

  .mp-capture-session-head,
  .mp-capture-import-progress-head,
  .mp-capture-code-actions { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
  .mp-capture-session-head :is(small, strong),
  .mp-capture-code :is(small, strong) { display: block; }
  .mp-capture-session-head small,
  .mp-capture-code small { color: var(--muted); font-size: var(--fs-caption); text-transform: uppercase; }
  .mp-capture-code { display: grid; gap: 10px; margin-top: 12px; padding: 12px; border: 1px solid color-mix(in srgb, var(--amber) 38%, var(--line)); border-radius: var(--r); background: var(--amber-soft); }
  .mp-capture-connection { display: grid; gap: 8px; }
  .mp-capture-connection input { width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--fs-caption); }
  .mp-capture-code-actions { align-items: flex-end; }
  .mp-capture-code-actions > span strong { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .08em; }
  .mp-capture-local-note { margin: 0; padding-top: 8px; border-top: 1px solid color-mix(in srgb, var(--amber) 26%, var(--line)); color: var(--amber); font-size: var(--fs-caption); line-height: 1.4; }
  .mp-capture-progress,
  .mp-capture-import-bar { height: 8px; overflow: hidden; border-radius: 999px; background: var(--surface-3); }
  .mp-capture-progress { margin-top: 12px; }
  .mp-capture-progress span { display: block; height: 100%; border-radius: inherit; background: var(--green); transition: width var(--t); }
  .mp-capture-import-progress { display: grid; gap: 8px; margin-top: 12px; padding: 10px 12px; border: 1px solid color-mix(in srgb, var(--blue) 26%, var(--line)); border-radius: var(--r); background: var(--blue-soft); }
  .mp-capture-import-progress-head :is(span, strong),
  .mp-capture-import-progress > small { font-size: var(--fs-caption); }
  .mp-capture-import-progress-head span,
  .mp-capture-import-progress > small { color: var(--muted); }
  .mp-capture-import-bar span { display: block; width: 34%; height: 100%; border-radius: inherit; background: var(--blue); animation: mp-capture-running 1.25s ease-in-out infinite; }
  .mp-capture-import-progress.is-complete { border-color: color-mix(in srgb, var(--green) 35%, var(--line)); background: var(--green-soft); }
  .mp-capture-import-progress.is-complete .mp-capture-import-bar span { width: 100%; background: var(--green); animation: none; }
  .mp-capture-import-progress.is-error { border-color: color-mix(in srgb, var(--red) 35%, var(--line)); background: var(--red-soft); }
  .mp-capture-import-progress.is-error .mp-capture-import-bar span { width: 100%; background: var(--red); animation: none; }
  .mp-capture-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
  .mp-capture-warning { margin: 12px 0 0; padding: 8px 10px; border-radius: var(--r-sm); background: var(--amber-soft); color: var(--amber); font-size: var(--fs-caption); }
  .mp-capture-preview { margin: 0 16px 16px; padding-top: 12px; border-top: 1px solid var(--line); }
  .mp-capture-preview > summary { font-weight: 700; cursor: pointer; }
  .mp-manual-decisions { display: grid; gap: 8px; }
  .mp-manual-decision { padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); }
  .mp-manual-decision.is-stale { color: var(--red); }
  .mp-legacy-protection {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: start;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid color-mix(in srgb, var(--green) 32%, var(--line));
    border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--green-soft) 35%, var(--surface));
  }
  .mp-legacy-protection strong { display: block; margin: 2px 0 4px; font-size: var(--fs-body); }
  .mp-legacy-protection p { margin: 0; color: var(--muted); font-size: var(--fs-caption); line-height: 1.5; }

  @keyframes mp-capture-running {
    0% { transform: translateX(-120%); }
    50% { transform: translateX(120%); }
    100% { transform: translateX(300%); }
  }

  /* ── Barra de revisión por lote: fija sobre la lista mientras se decide
     (mismo criterio que .annex-controls en toolbar.css: sticky es geometría
     de vista). ── */
  .bank-batch-review-bar {
    position: sticky; top: 10px; z-index: var(--z-sticky);
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: 16px; margin-bottom: 12px; padding: 12px 16px;
    border: 1px solid var(--line); border-radius: var(--r-lg);
    background: var(--surface); box-shadow: var(--shadow-sm);
    overflow: hidden;
  }
  .bank-batch-review-bar > label { display: flex; align-items: center; gap: 10px; cursor: pointer; }
  .bank-batch-review-bar > label input,
  .bank-movement-select-cell input { width: 18px; height: 18px; accent-color: var(--accent); }
  .bank-batch-review-bar > label :is(strong, small) { display: block; }
  .bank-batch-review-bar > label small,
  .bank-batch-review-bar [data-bank-selection-count] { color: var(--muted); font-size: var(--fs-caption); }
  .bank-batch-review-bar > div { display: flex; align-items: center; gap: 10px; }
  .bank-batch-review-progress { position: absolute; right: 0; bottom: 0; left: 0; height: 4px; background: var(--green-soft); }
  .bank-batch-review-progress > span { display: block; width: 0; height: 100%; background: var(--green); transition: width var(--t); }

  /* ── Conciliación: operativo | financiero lado a lado cuando cada columna
     alcanza los 620px reales (auto-fit); si no, se apilan. ── */
  .bank-account-columns {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(660px, 1fr));
    gap: 16px; align-items: start; margin-top: 12px;
  }
  .bank-account-group { display: grid; gap: 10px; }
  .bank-account-group.is-financing { margin-top: 14px; }
  .bank-account-columns .bank-account-group.is-financing { margin-top: 0; }
  .bank-statement-list { container-type: inline-size; }
  .bank-statement-card.is-financing,
  .bank-batch-account.is-financing { border-color: color-mix(in srgb, var(--blue) 20%, var(--line)); }
  .bank-statement-card > summary { cursor: pointer; list-style: none; transition: background var(--t); }
  .bank-statement-card > summary::-webkit-details-marker { display: none; }
  .bank-statement-card > summary:hover { background: var(--surface-2); }

  /* ── Cabecera de cartola: rótulo · franja de saldos · estado. ── */
  .bank-statement-card-head {
    display: grid;
    grid-template-columns: minmax(220px, 1fr) minmax(420px, 1.7fr) auto 20px;
    align-items: center; gap: 14px; padding: 12px 16px 12px 46px;
  }
  .bank-statement-card-head > div:first-child {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
  }
  .bank-statement-card-head h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .bank-statement-card-head small { display: block; margin: 0; overflow: hidden; color: var(--muted); font-size: var(--fs-caption); text-overflow: ellipsis; white-space: nowrap; }
  .bank-statement-card-head > div:first-child small { order: 2; }
  .bank-statement-card-head h3 { margin: 0; font-size: var(--fs-body-lg); }
  .bank-statement-card.is-missing .bank-statement-card-head { grid-template-columns: 1fr auto; }
  .bank-statement-card.is-missing { background: var(--surface-2); }
  .bank-statement-card.is-missing p { margin: 0; padding: 0 16px 14px 46px; color: var(--muted); font-size: var(--fs-small); }
  .bank-statement-card-head > div:first-child::before {
    content: "";
    position: absolute;
    top: 50%;
    left: -32px;
    width: 20px;
    height: 20px;
    border: 1px solid var(--line-2);
    border-radius: 50%;
    background: var(--surface);
    transform: translateY(-50%);
  }
  .bank-statement-card.is-pending .bank-statement-card-head > div:first-child::before { border-color: var(--amber); }
  .bank-statement-card.is-complete .bank-statement-card-head > div:first-child::before {
    content: "✓";
    border-color: var(--green);
    background: var(--green);
    color: var(--surface);
    font-size: var(--fs-caption);
    font-weight: 800;
    line-height: 18px;
    text-align: center;
  }
  .bank-statement-card > summary.bank-statement-card-head::after {
    content: "";
    width: 8px;
    height: 8px;
    border-right: 1px solid var(--muted);
    border-bottom: 1px solid var(--muted);
    transform: translateY(-2px) rotate(45deg);
    transition: transform var(--t);
  }
  .bank-statement-card[open] > summary.bank-statement-card-head::after { transform: translateY(2px) rotate(225deg); }
  .bank-balance-strip { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px; }
  .bank-balance-strip > div { min-width: 0; padding: 0 10px; border-left: 1px solid var(--line); }
  .bank-balance-strip > div:first-child { border-left: 0; }
  .bank-balance-strip :is(small, strong) { display: block; }
  .bank-balance-strip small { color: var(--muted); font-size: var(--fs-caption); }
  .bank-balance-strip strong { margin-top: 2px; font-size: var(--fs-body); }
  .bank-balance-note { margin: 8px 0 0; color: var(--muted); font-size: var(--fs-caption); line-height: 1.4; }

  /* ── Fila de movimiento: casilla · fecha · glosa · monto · clasificación ·
     acción — ver "Decisión de fila de movimiento" arriba. El scroll propio
     es la red de seguridad, nunca se usa overflow:hidden aquí. ── */
  .bank-statement-movement-body { overflow-x: auto; }
  .bank-movement-list-head,
  .bank-movement-row-main {
    display: grid;
    grid-template-columns: 24px 80px minmax(140px, 1fr) 96px minmax(160px, 1fr) 64px;
    align-items: center; gap: 8px; padding: 0 16px;
  }
  .bank-movement-list-head { min-height: 34px; }
  .bank-movement-list-head {
    border-bottom: 1px solid var(--line); background: var(--surface-2);
    color: var(--muted); font-size: var(--fs-caption); font-weight: 700;
    letter-spacing: .05em; text-transform: uppercase;
  }
  .bank-movement-row { border-bottom: 1px solid var(--line); background: var(--surface); }
  .bank-movement-row:last-child { border-bottom: 0; }
  .bank-movement-row-main { min-height: 52px; padding-top: 6px; padding-bottom: 6px; }
  .bank-movement-row:not(.is-expanded) .bank-movement-row-main:hover { background: var(--surface-2); }
  .bank-movement-row.is-review { background: color-mix(in srgb, var(--amber-soft) 30%, var(--surface)); }
  .bank-movement-row.is-selected-for-batch { box-shadow: inset 3px 0 0 var(--accent); }
  .bank-movement-row.is-saving-batch { opacity: .62; }
  .bank-movement-row.is-focused { scroll-margin-top: 90px; box-shadow: inset 0 0 0 2px var(--accent); }
  .bank-movement-row.is-expanded .bank-movement-row-main { background: var(--surface-2); }
  .bank-movement-select-cell { text-align: center; }
  .bank-movement-identity,
  .bank-movement-classification-summary { min-width: 0; }
  .bank-movement-identity strong,
  .bank-movement-identity small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .bank-movement-identity strong { font-size: var(--fs-body); }
  .bank-movement-identity small { margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); }
  .bank-movement-row time { color: var(--muted); font-size: var(--fs-caption); font-variant-numeric: tabular-nums; white-space: nowrap; }
  .bank-movement-amount { text-align: right; font-size: var(--fs-body); font-variant-numeric: tabular-nums; white-space: nowrap; }
  .bank-movement-classification-summary { display: flex; align-items: center; gap: 8px; }
  .bank-movement-classification-summary > strong {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    font-weight: 650;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .bank-movement-classification-summary .bank-row-status { flex: none; }
  .bank-movement-toggle {
    min-height: 30px; padding: 4px 9px; border: 1px solid var(--line);
    border-radius: var(--r-sm); background: var(--surface); color: var(--ink-2);
    font-size: var(--fs-caption); font-weight: 700; cursor: pointer;
    transition: background var(--t), border-color var(--t), color var(--t), transform var(--t);
  }
  .bank-movement-toggle:hover,
  .bank-movement-toggle[aria-expanded="true"] { border-color: var(--ink); background: var(--surface-2); color: var(--ink); }
  .bank-movement-toggle:active { transform: scale(.96); }

  /* ── Editor de movimiento: contrapartida | acción (fila 1), meta a lo
     ancho (fila 2); documentos | respaldo contable a los lados. ── */
  .bank-movement-editor { padding: 12px 16px 16px 48px; }
  .bank-movement-editor-primary {
    display: grid;
    grid-template-columns: minmax(240px, 1fr) auto;
    align-items: end; gap: 10px;
  }
  .bank-movement-editor-primary .primary-action { grid-column: 2; grid-row: 1; justify-self: end; }
  .bank-movement-editor-primary .bank-classification-meta,
  .bank-movement-editor-meta { grid-column: 1 / -1; }
  .bank-movement-editor-support {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(300px, 1.1fr);
    align-items: start; gap: 10px; margin-top: 10px;
  }
  .bank-movement-editor-support > .bank-evidence-control:only-child { grid-column: 2; }
  .bank-movement-editor[hidden] { display: none; }
  .bank-movement-editor { border-top: 1px solid var(--line); background: var(--surface-2); }
  .bank-classification-meta {
    padding: 7px 9px; border: 1px solid var(--line); border-radius: var(--r-sm);
    background: var(--surface); font-size: var(--fs-caption); line-height: 1.35;
  }
  .bank-classification-meta :is(strong, span) { display: block; }
  .bank-classification-meta span { margin-top: 2px; color: var(--muted); }
  .bank-classification-meta.is-suggestion { border-color: color-mix(in srgb, var(--amber) 35%, var(--line)); }

  /* ── Documentos vinculados y respaldo contable. ── */
  .bank-document-option {
    display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center; gap: 10px; padding: 8px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2);
  }
  .bank-document-option.is-selected { border-color: color-mix(in srgb, var(--green) 42%, var(--line)); background: var(--green-soft); }
  .bank-document-option > input { width: 16px; height: 16px; }
  .bank-document-option span > :is(strong, small) { display: block; }
  .bank-document-option span > small { margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); }
  .bank-document-links { max-width: 520px; margin-top: 8px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
  .bank-document-links summary { display: flex; justify-content: space-between; gap: 10px; padding: 8px 10px; cursor: pointer; list-style: none; }
  .bank-document-links summary::-webkit-details-marker { display: none; }
  .bank-document-links summary :is(strong, span) { font-size: var(--fs-caption); }
  .bank-document-links summary strong { color: var(--accent); }
  .bank-document-links summary span { color: var(--muted); text-transform: uppercase; }
  .bank-document-options { display: grid; gap: 6px; padding: 0 8px 8px; }
  .bank-document-policy { display: block; padding: 0 10px 9px; color: var(--muted); font-size: var(--fs-caption); line-height: 1.4; }
  .bank-remember-rule { grid-column: 1 / -1; display: flex; align-items: center; gap: 7px; color: var(--ink-2); font-size: var(--fs-caption); font-weight: 650; }
  .bank-remember-rule input { width: 16px; height: 16px; }
  .bank-evidence-control {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px; padding: 10px;
  }

  /* ── Deuda derivada de Mercado Pago: fila-botón fecha · glosa · monto. ── */
  .bank-derived-debt-body > button {
    display: flex; align-items: center; justify-content: space-between;
    gap: 16px; width: 100%; min-height: 52px; padding: 8px 16px;
    border: 0;
    border-bottom: 1px solid var(--line);
    background: var(--surface);
    color: var(--ink);
    text-align: left;
    cursor: pointer;
    transition: background var(--t), padding-left var(--t);
  }
  .bank-derived-debt-body > button:last-child { border-bottom: 0; }
  .bank-derived-debt-body > button:hover { padding-left: 20px; background: var(--surface-2); }
  .bank-derived-debt-body > button > span { min-width: 0; }
  .bank-derived-debt-body :is(time, strong, small) { display: block; }
  .bank-derived-debt-body time { color: var(--muted); font-size: var(--fs-caption); }
  .bank-derived-debt-body strong { margin-top: 2px; overflow: hidden; font-size: var(--fs-body); text-overflow: ellipsis; white-space: nowrap; }
  .bank-derived-debt-body small { margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); }
  .bank-derived-debt-body b { flex: none; font-size: var(--fs-body); font-variant-numeric: tabular-nums; white-space: nowrap; }
  .bank-derived-debt-body .is-negative { color: var(--red); }
  .bank-derived-debt-body .is-positive { color: var(--green); }

  /* Los acordeones de esta mesa conservan contexto al desplegarse: el cuerpo
     crece desde la cabecera y acompaña con un desplazamiento/opacidad mínimos.
     La consulta @supports deja apertura nativa instantánea en navegadores que
     todavía no exponen ::details-content. El reset global neutraliza todo el
     movimiento cuando el sistema pide prefers-reduced-motion. */
  @supports selector(details::details-content) {
    #view-bank-statements { interpolate-size: allow-keywords; }

    details.bank-source-drawer::details-content,
    details.bank-statement-card::details-content,
    details.bank-batch-account::details-content,
    details.mp-capture-detail::details-content,
    details.mp-capture-help::details-content,
    details.bank-document-links::details-content {
      block-size: 0;
      overflow-y: clip;
      opacity: 0;
      transform: translateY(-5px);
      transition:
        block-size var(--t-lg),
        content-visibility .26s allow-discrete,
        opacity var(--t),
        transform var(--t-lg);
    }

    details.bank-source-drawer[open]::details-content,
    details.bank-statement-card[open]::details-content,
    details.bank-batch-account[open]::details-content,
    details.mp-capture-detail[open]::details-content,
    details.mp-capture-help[open]::details-content,
    details.bank-document-links[open]::details-content {
      block-size: auto;
      opacity: 1;
      transform: translateY(0);
    }
  }

  /* Cada columna de cuentas decide su propia forma según el espacio real,
     no según el ancho total del navegador. */
  @container (max-width: 1080px) {
    .bank-statement-card-head { grid-template-columns: minmax(0, 1fr) auto 20px; }
    .bank-statement-card-head .bank-balance-strip { grid-column: 1 / -1; grid-row: 2; }
    .bank-statement-card-head > .finance-status-badge { grid-column: 2; grid-row: 1; }
    .bank-statement-card > summary.bank-statement-card-head::after { grid-column: 3; grid-row: 1; }
    .bank-balance-strip > div:first-child { padding-left: 0; border-left: 0; }
    .bank-movement-list-head { display: none; }
    .bank-movement-row-main { grid-template-columns: 24px 80px minmax(150px, 1fr) 104px 60px; }
    .bank-movement-classification-summary { grid-column: 3 / 5; grid-row: 2; padding-bottom: 2px; }
    .bank-movement-toggle { grid-column: 5; grid-row: 1 / span 2; }
    .bank-movement-editor-support { grid-template-columns: 1fr; }
    .bank-movement-editor-support > .bank-evidence-control:only-child { grid-column: 1; }
  }

  /* ── Responsive: formularios y grillas secundarias se apilan para lectura
     cómoda (no por falta de espacio técnico: leer un form en una columna es
     más claro incluso cuando la fila de movimiento arriba ya cabe sola). ── */
  @media (max-width: 900px) {
    #view-bank-statements .financial-operation-grid,
    .bank-batch-account-body,
    .mp-statement-upload-form,
    .mp-capture-stats,
    .mp-capture-layout,
    .mp-ambiguity-group-grid,
    .bank-evidence-control,
    .bank-movement-editor-support { grid-template-columns: 1fr; }
    .bank-batch-account-body .bank-batch-empty { grid-column: 1; }
    .bank-movement-editor-support > .bank-evidence-control:only-child { grid-column: 1; }
    .mp-source-blocker { align-items: flex-start; flex-direction: column; }
    .mp-source-blocker span { text-align: left; }
    .mp-source-results,
    .bank-balance-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .bank-batch-review-bar { position: static; flex-direction: column; align-items: stretch; }
    .bank-document-option { grid-template-columns: auto minmax(0, 1fr); }
    .bank-document-amount { grid-column: 2; }
  }

  /* Formulario de carga: separa la operación en curso del bloque siguiente
     (su grilla interna .financial-operation-grid ya vive más arriba). */
  .financial-operation-form { margin-bottom: 16px; }

  @media (max-width: 680px) {
    .bank-source-drawer-body { padding: 12px; gap: 16px; }
    .bank-batch-account-head { flex-wrap: wrap; }
  }
}

/* líneas: ~267 de presupuesto 150 — excedido a propósito: como en
   pedidos.css/tareas-diarias.css, esta vista es la única dueña de la
   identidad visual del drawer, las tarjetas de cuenta/paso y la fila de
   movimiento (ningún componente de 04-componentes las reclama; verificado
   con grep). Es además la vista más densa del sistema por diseño del
   producto (config. del período + 2 tipos de cuenta + Mercado Pago con
   captura/cartola/cruce en un solo lienzo). */
/* ══ cierre-de-turno (#view-reconciliation) ═════════════════════════════════
   Cubre: .reconciliation-note, .reconciliation-layout, .reconciliation-cal-nav,
     .reconciliation-month-pill, .reconciliation-today-btn,
     .reconciliation-calendar-head/-grid, .reconciliation-day(--blank),
     .reconciliation-day-number, .reconciliation-day-shifts,
     .reconciliation-day-shift(.ok/.warn/.bad/.info), .reconciliation-calendar-empty,
     .reconciliation-detail-head, .reconciliation-shift-picker,
     .reconciliation-shift-content, .reconciliation-shift-metrics(+article/small/
     strong), .reconciliation-subhead(-cash), .reconciliation-cash-movement-panel,
     .reconciliation-movement-table .table-action, .reconciliation-comment-field,
     .reconciliation-amount-input, .reconciliation-keypad(-display/-grid/button)
   Fuente v1: styles.css ~L2762-3213 (bloque principal) fusionada con
     ~L10370 (colapso 980→900), ~L10541 (métricas 980→900), ~L10956-10978
     (calendario 620→680: gap y padding, sin la reducción de fuente del chip
     de turno — 10px queda fuera de la escala cerrada, se sostiene 11px).
   Casos límite: nota de cabecera larga → max-width + wrap; grilla de
     calendario vacía por error de carga → mensaje ocupa las 7 columnas;
     celda de día sin turnos usa el tono muted por defecto del chip (sin
     regla extra); cifra ausente en las métricas del turno llega como "—"
     desde JS (no es geometría).
   Nota de propiedad: .reconciliation-day, .reconciliation-day-shift,
     .reconciliation-shift-metrics y .reconciliation-keypad no son instancias
     de ningún componente de 04-componentes (verificado en los "Cubre" de
     badge/kpi/panel/popover) — son widgets únicos de esta vista, sin dueño
     arriba en la cascada. Por eso fijan también su identidad mínima (borde,
     tinte semántico, tipografía) con tokens, no solo su acomodo. El resto de
     la vista SÍ reutiliza componentes existentes tal cual (.tool-panel,
     .table-section, .pill, .icon-button, .segmented-control, .stack-form,
     .table-wrap, .table-inline-field, .book-summary/.sales-summary → KPI,
     .reconciliation-shift-status → badge.css "placas de estado").
   Excluidas a propósito (re-skin de componente ya resuelto, no toca esta
     capa): tamaño de fuente de .reconciliation-nav-arrow y
     .reconciliation-add-movement (variantes de .icon-button — tipografía es
     de boton.css); tamaño/tipografía de los botones de .reconciliation-shift-
     picker (variante de .segmented-control — toolbar.css); el reposicionamiento
     del keypad a 680px con !important de v1 — ya lo resuelve inline JS
     (positionReconciliationKeypad) por matchMedia, la regla CSS era deuda
     peleando contra su propio JS.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  .reconciliation-note { margin-top: 8px; max-width: 760px; }
  .reconciliation-section { min-width: 0; }

  /* Los dos lados del flujo conservan la misma superficie que el resto de
     herramientas, aunque el layout actual los apila para dar aire al
     calendario de siete columnas. */
  .reconciliation-calendar-panel,
  .reconciliation-detail-panel,
  .reconciliation-entry-panel {
    min-width: 0;
    padding: 16px;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-xs);
  }
  .reconciliation-calendar-panel { width: 100%; }
  .reconciliation-entry-panel { margin-top: 12px; }

  /* ── Esqueleto: calendario arriba, detalle abajo, ambos a todo el ancho de
     la vista — el calendario necesita las 7 columnas completas y el detalle
     trae formulario + 2 tablas, ninguno cabe partido a la mitad. ── */
  .reconciliation-layout {
    margin-top: 16px;
    display: grid;
    grid-template-columns: 1fr;
    gap: 14px;
    align-items: start;
  }

  /* ── Barra de navegación del mes ── */
  .reconciliation-cal-nav { display: flex; align-items: center; gap: 6px; margin-bottom: 14px; }
  .reconciliation-nav-arrow { font-size: var(--fs-h3); font-weight: 400; }
  .reconciliation-month-pill { flex: 1; justify-content: center; text-transform: capitalize; pointer-events: none; }
  .reconciliation-today-btn { margin-left: auto; }

  /* ── Grilla de calendario: 7 columnas fijas (días de la semana) ── */
  .reconciliation-calendar-head,
  .reconciliation-calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 8px;
  }
  .reconciliation-calendar-head { margin-top: 12px; }
  .reconciliation-calendar-grid { margin-top: 8px; }
  .reconciliation-calendar-empty { grid-column: 1 / -1; }

  /* ── Celda de día (widget propio, ver Nota de propiedad) ── */
  .reconciliation-day,
  .reconciliation-day--blank { min-height: 116px; border-radius: var(--r); }
  .reconciliation-day {
    display: grid; align-content: start; gap: 6px; padding: 10px;
    border: 1px solid var(--line); background: var(--surface); text-align: left;
    transition: border-color var(--t), transform var(--t);
  }
  .reconciliation-day:hover { border-color: var(--line-strong); transform: translateY(-1px); }
  .reconciliation-day.is-selected { border-color: var(--ink); box-shadow: 0 0 0 2px var(--line-2); }
  .reconciliation-day.is-today { border-color: var(--amber-bright); }
  .reconciliation-day--blank { background: transparent; }
  .reconciliation-day-number { font-size: var(--fs-h2); font-weight: 800; line-height: 1; }
  .reconciliation-day-shifts { display: grid; gap: 6px; margin-top: auto; }

  /* Chip de turno AM/PM: misma fórmula de tinte que .reco-badge (badge.css),
     forma de bloque propia — no es una instancia de esa píldora. */
  .reconciliation-day-shift {
    display: block; padding: 6px 8px; border-radius: var(--r-sm);
    background: var(--surface-2); color: var(--muted);
    font-size: var(--fs-caption); font-weight: 700;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .reconciliation-day-shift.ok   { background: var(--green-soft); color: var(--green); }
  .reconciliation-day-shift.warn { background: var(--amber-soft); color: var(--amber); }
  .reconciliation-day-shift.bad  { background: var(--red-soft);   color: var(--red); }
  .reconciliation-day-shift.info { background: var(--blue-soft);  color: var(--blue); }

  /* ── Cabecera del panel de detalle: el título (eyebrow + h2) crece más que
     el selector AM/PM, así que el selector se centra en vez de estirarse. ── */
  .reconciliation-detail-head { align-items: flex-start; }
  .reconciliation-shift-picker { align-self: center; height: auto; }

  /* Ritmo vertical único del contenido del turno: el gap reemplaza los
     márgenes sueltos que cada hijo traía en v1. */
  .reconciliation-shift-content { margin-top: 16px; display: grid; gap: 12px; }

  /* Métricas del turno (POS/Ingresado/Diferencia/Pagos): no usa .book-summary
     ni ninguna tira KPI existente — 4 columnas iguales, widget propio. */
  .reconciliation-shift-metrics { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
  .reconciliation-shift-metrics article {
    border: 1px solid var(--line); border-radius: var(--r); padding: 10px 12px; background: var(--surface);
  }
  .reconciliation-shift-metrics small { display: block; color: var(--muted); font-size: var(--fs-caption); font-weight: 700; }
  .reconciliation-shift-metrics strong { display: block; margin-top: 6px; font-size: var(--fs-h3); font-weight: 800; }

  /* ── Subcabeceras del formulario (propina por medio / caja chica) ── */
  .reconciliation-subhead { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 16px 0 10px; }
  .reconciliation-shift-rule-label {
    margin: 0 0 10px; color: var(--muted); font-size: var(--fs-caption);
    font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  }
  .reconciliation-cash-subhead { margin-top: 24px; padding-top: 18px; border-top: 1px solid var(--line); }
  .reconciliation-cash-movement-panel { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line); }

  /* El botón "quitar" no se estira en la columna angosta de Acción */
  .reconciliation-movement-table .table-action { min-width: 0; white-space: nowrap; }
  .reconciliation-manual-table { min-width: 100%; }
  .reconciliation-add-movement { width: 36px; flex: 0 0 36px; font-size: var(--fs-h3); }

  /* Anchos de columna de las 3 tablas del formulario */
  .reconciliation-comment-field { min-width: 220px; }
  .reconciliation-amount-input { min-width: 108px; max-width: 124px; }
  .reconciliation-form-note { margin: 0; color: var(--muted); font-size: var(--fs-caption); line-height: 1.45; }

  /* ── Keypad numérico flotante (widget propio): la posición (left/top) la
     calcula positionReconciliationKeypad() en JS; acá solo marco + estados. ── */
  .reconciliation-keypad {
    position: fixed; z-index: var(--z-popover);
    width: 316px; max-width: calc(100vw - 20px); padding: 12px;
    border: 1px solid var(--line-2); border-radius: var(--r-lg);
    background: var(--surface); box-shadow: var(--shadow-md);
  }
  .reconciliation-keypad-display {
    min-height: 42px; display: flex; align-items: center; justify-content: flex-end;
    margin-bottom: 10px; padding: 0 12px;
    border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2);
    color: var(--ink); font-size: var(--fs-h2); font-weight: 800; text-align: right;
  }
  .reconciliation-keypad-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
  .reconciliation-keypad button {
    min-height: 52px; border: 1px solid var(--line-2); border-radius: var(--r); background: var(--surface);
    color: var(--ink); font-size: var(--fs-h3); font-weight: 800; cursor: pointer;
    transition: border-color var(--t), background var(--t);
  }
  .reconciliation-keypad button:hover:not(:disabled) { border-color: var(--accent); background: var(--blue-soft); }
  .reconciliation-keypad button:disabled { opacity: .38; cursor: not-allowed; }
  .reconciliation-keypad .is-primary { background: var(--blue); border-color: var(--blue); color: var(--surface); }

  /* ── Responsive ── */
  @media (max-width: 900px) {
    .reconciliation-shift-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  @media (max-width: 680px) {
    .reconciliation-calendar-head,
    .reconciliation-calendar-grid { gap: 4px; }
    .reconciliation-day { padding: 8px; }
  }
}

/* líneas: ~165 de presupuesto 150 — excedido a propósito: 4 de los widgets
   (celda de día, chip de turno, tira de métricas, keypad) no tienen
   componente dueño en 04-componentes, así que esta vista es también su
   única identidad visual, no solo su geometría (mismo criterio que
   05-vistas/tareas-diarias.css con la tarjeta del kanban). */
/* ══ vista · cierre del mes ═══════════════════════════════════════════════
   data-view="financial-close" — #view-financial-close
   Cubre: .financial-close-hero(-main/-meta), .financial-close-kpis,
     .financial-blockers(.is-clear), .financial-close-section(>summary),
     .financial-source-grid, .financial-source-card(.is-green/.is-red/
     .is-muted/-head/-amount/-reviewer), .financial-source-integration
     (.is-green/.is-red), .financial-source-module-link, .financial-source-
     lock, .financial-source-review (solo grilla), .financial-source-
     override-warning, .financial-opening-summary(>div), .financial-opening-
     table-wrap/-table (solo anchos), .financial-books-link.
   El resto de la vista ya lo resuelven los componentes: el bloque de
     período (.header-period-nav/.financial-close-month-picker, prev/next)
     lo reubica JS (relocatePeriodToHeader) dentro de .page-header — no hay
     geometría de cabecera que fijar aquí; .finance-panel-head/.eyebrow/
     .section-note → panel.css; .finance-status-badge(.is-*) → badge.css;
     .primary-action/.secondary-action/.table-action/.icon-button →
     boton.css; inputs/selects (incl. .financial-money-input, los <select>
     de override y de apertura) → campo.css/base; tipografía y padding de
     <table>/th/td → base + tabla.css (fuera de esta capa por prohibición
     §8, aplica también a tablas sin dueño). Familias .financial-entry-* y
     .financial-
     journal-* NO son de esta vista (verificado en app.js: solo los usa
     renderAccounting, la vista "Contabilidad"); .flow-head .icon-button es
     de "Estado de resultados" (NOTAS-P2 #1), no se resuelve aquí.
   Fuente v1: styles.css ~L9918-9993 (hero/kpis/blockers), ~L9995-10132
     (secciones/checklist/apertura), ~L10181-10212 (responsive 1050→900,
     720→680, consolidados a 1100/680), ~L15962-15965 (financial-source-
     integration .accounting-entry-link, financial-books-link), ~L16252
     (financial-source-module-link) — fusionadas.
   Nota de propiedad: .financial-close-hero, .financial-close-kpis article,
     .financial-blockers, .financial-close-section, .financial-source-card,
     .financial-source-integration, .financial-source-lock/-override-
     warning y .financial-opening-summary>div son tarjetas sin dueño en
     04-componentes (verificado en los "Cubre" de badge/kpi/panel) y
     exclusivas de esta vista: fijan también su identidad mínima (borde,
     tinte semántico, tipografía) con tokens, no solo su acomodo — mismo
     criterio que 05-vistas/cierre-de-turno.css.
   Casos límite: descripción de fuente corta/larga → min-height en <p>
     sostiene alineada la fila del badge de integración entre las dos
     tarjetas vecinas del grid; aviso de override inválido es condicional
     (JS omite el bloque, no hay contenedor vacío que posicionar); periodo
     cerrado → inputs/selects llegan disabled (estado de campo.css, la
     geometría no cambia); apertura sin partidas → tabla sin filas, sin
     estado vacío propio en el HTML de v1 (deuda no resuelta en esta capa).
   Cambios visibles: sin sombra permanente en las tarjetas (receta canon
     de panel.css); degradado del hero usa var(--surface) en vez del
     #f8fafc suelto de v1 (mismo tono, ya no es hex crudo); paddings/gaps
     fuera de la escala (13, 15, 18, 7, 9) se llevan al múltiplo de 2 más
     cercano; font-size fuera de escala (20, 9.5, 9, 10, 10.5) al token
     más próximo; font-weight 760/680 → 800/700.
   ═══════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Hero: estado del periodo + versión + acción principal ────────────── */
  .financial-close-hero {
    display: flex; align-items: center; justify-content: space-between; gap: 20px;
    margin-top: 16px; padding: 20px;
    border: 1px solid var(--line); border-radius: var(--r);
    background: linear-gradient(135deg, var(--surface) 0%, var(--blue-soft) 100%);
  }
  .financial-close-hero-main,
  .financial-close-hero-meta { display: flex; align-items: center; gap: 14px; }
  .financial-close-hero-main h3 { margin: 0 0 4px; font-size: var(--fs-h2); }
  .financial-close-hero-main p { margin: 0; color: var(--muted); }
  .financial-close-hero-meta > span { color: var(--muted); font-size: var(--fs-caption); font-weight: 700; }

  /* ── KPIs del cierre: 4 cifras iguales (comparte tipografía con el
     resumen de apertura — misma jerarquía visual, misma familia v1) ────── */
  .financial-close-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin: 14px 0; }
  .financial-close-kpis article {
    display: grid; gap: 6px; min-width: 0; padding: 16px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
  }
  .financial-close-kpis small,
  .financial-opening-summary small {
    color: var(--muted); font-size: var(--fs-caption); font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  }
  .financial-close-kpis strong,
  .financial-opening-summary strong { font-size: var(--fs-h2); font-variant-numeric: tabular-nums; }
  .financial-close-kpis span,
  .financial-opening-summary span { color: var(--muted); font-size: var(--fs-caption); line-height: 1.35; }

  /* ── Aviso de bloqueo / controles completos ────────────────────────────── */
  .financial-blockers {
    display: flex; align-items: flex-start; gap: 12px; margin-bottom: 14px; padding: 12px 16px;
    border: 1px solid color-mix(in srgb, var(--red) 24%, transparent); border-radius: var(--r-sm);
    background: var(--red-soft); color: var(--ink-2); font-size: var(--fs-caption); line-height: 1.45;
  }
  .financial-blockers strong { flex: none; color: var(--red); }
  .financial-blockers.is-clear { border-color: color-mix(in srgb, var(--green) 24%, transparent); background: var(--green-soft); }
  .financial-blockers.is-clear strong { color: var(--green); }

  /* ── Secciones del cierre: checklist / apertura (<details>) / libros ──── */
  .financial-close-section {
    margin-top: 14px; padding: 16px;
    border: 1px solid var(--line); border-radius: var(--r); background: var(--surface);
  }
  .financial-close-section > summary { display: flex; align-items: center; justify-content: space-between; gap: 16px; cursor: pointer; list-style: none; }
  .financial-close-section > summary::-webkit-details-marker { display: none; }
  .financial-close-section > summary h3 { margin: 2px 0 4px; font-size: var(--fs-h3); }
  .financial-close-section > summary span { color: var(--muted); font-size: var(--fs-caption); }
  .financial-close-section[open] > summary { padding-bottom: 16px; border-bottom: 1px solid var(--line); }

  /* ── Checklist: grilla de tarjetas de fuente (2 columnas) ──────────────── */
  .financial-source-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }

  .financial-source-card {
    display: grid; gap: 10px; min-width: 0; padding: 14px;
    border: 1px solid var(--line); border-left: 3px solid var(--amber);
    border-radius: var(--r-sm); background: var(--surface-2);
  }
  .financial-source-card.is-green { border-left-color: var(--green); }
  .financial-source-card.is-red { border-left-color: var(--red); }
  .financial-source-card.is-muted { border-left-color: var(--muted-2); }

  .financial-source-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
  .financial-source-card-head small { color: var(--muted); font-size: var(--fs-caption); font-weight: 800; letter-spacing: .05em; text-transform: uppercase; }
  /* min-height: la descripción varía en largo; sostiene la fila de integración alineada entre las 2 tarjetas de la fila */
  .financial-source-card p { min-height: 32px; margin: 0; color: var(--muted); font-size: var(--fs-caption); line-height: 1.45; }
  .financial-source-amount { color: var(--ink-2); font-size: var(--fs-body); font-variant-numeric: tabular-nums; }
  .financial-source-reviewer { color: var(--muted); font-size: var(--fs-caption); }

  .financial-source-integration {
    display: flex; align-items: center; gap: 8px; min-width: 0; padding: 8px 10px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
  }
  .financial-source-integration small { overflow: hidden; color: var(--muted); font-size: var(--fs-caption); line-height: 1.35; text-overflow: ellipsis; }
  .financial-source-integration.is-green { border-color: color-mix(in srgb, var(--green) 24%, transparent); background: var(--green-soft); }
  .financial-source-integration.is-red { border-color: color-mix(in srgb, var(--red) 24%, transparent); background: var(--red-soft); }
  /* "Ver asiento" (accounting-entry-link, base en otra vista): aquí solo se posiciona al final de la fila */
  .financial-source-integration .accounting-entry-link { margin-left: auto; white-space: nowrap; }

  /* El botón de módulo (banco/activos) no se estira en la grilla de 1 columna de la tarjeta */
  .financial-source-module-link { align-self: start; }

  .financial-source-lock,
  .financial-source-override-warning {
    padding: 8px 10px; border-radius: var(--r-sm);
    background: var(--amber-soft); color: var(--amber); font-size: var(--fs-caption); line-height: 1.4;
  }

  /* Fila de revisión: select de estado · nota de respaldo · botón guardar */
  .financial-source-review { display: grid; grid-template-columns: minmax(145px, .7fr) minmax(180px, 1.3fr) auto; gap: 8px; }

  /* ── Apertura: resumen de 3 cifras + tabla de partidas ─────────────────── */
  .financial-opening-summary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin: 14px 0; }
  .financial-opening-summary > div {
    display: grid; gap: 4px; padding: 12px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2);
  }

  .financial-opening-table-wrap { overflow-x: auto; }
  .financial-opening-table { min-width: 1050px; }
  .financial-opening-table td:first-child { min-width: 220px; }
  .financial-opening-table td:first-child small { display: block; margin-top: 2px; }
  .financial-opening-table .financial-money-input { width: 125px; text-align: right; }
  .financial-opening-table td:nth-child(5) input { min-width: 240px; }

  /* ── Cierre a libros contables ──────────────────────────────────────────── */
  .financial-books-link { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
  .financial-books-link span { color: var(--muted); font-size: var(--fs-caption); }

  /* ── Responsive ─────────────────────────────────────────────────────────── */
  @media (max-width: 1100px) {
    .financial-close-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .financial-source-review { grid-template-columns: 1fr; }
  }

  @media (max-width: 680px) {
    .financial-close-hero,
    .financial-close-hero-main,
    .financial-close-hero-meta { align-items: flex-start; flex-direction: column; }
    .financial-close-kpis,
    .financial-source-grid,
    .financial-opening-summary { grid-template-columns: 1fr; }
    .financial-blockers { flex-direction: column; }
  }
}

/* líneas: ~179 de presupuesto 150 — excedido a propósito: 8 tarjetas sin
   dueño en 04-componentes (hero, kpis, blockers, section, source-card,
   source-integration, lock/warning, opening-summary) fijan también su
   identidad mínima, no solo su acomodo (mismo criterio que cierre-de-
   turno.css/tareas-diarias.css) */
/* ══ vista · comparador de precios ═══════════════════════════════════════
   data-view="prices" — #view-prices
   Cubre: #view-prices .benchmark-head, #view-prices .benchmark-tools (grid
     fino de 5 campos — el campo en sí, .benchmark-field, ya vive completo
     en campo.css: filas, variantes --search/--period, hint, responsive);
     detalle al clic (modal fuera de #view-prices en el DOM) —
     .benchmark-detail-context, .benchmark-detail-key(+--price/--best/
     --saving), .report-chart.benchmark-detail-chart, .benchmark-detail-
     markers, .benchmark-marker-row.
   Fuente v1: styles.css ~L2691-2760 (contexto/leyenda/gráfico/marcadores
     del detalle), ~L3610 (benchmark-head), ~L3622-3629 (grid de tools),
     ~L10401-10429+10509-10514 (responsive 980), ~L14169-14177 (banda
     621-980: grid compacto de una fila) — fusión last-wins, consolidada a
     los 3 breakpoints canon. ~L3677-3681+10437-10439+14176 (.benchmark-
     refresh): MUERTO, sin emisor en index.php/app.js actual — no se porta.
   Comparte con #view-consumption: #consumptionTrendModal reutiliza LITERAL
     las 5 clases .benchmark-detail-* y .benchmark-marker-row (ver app.js
     renderConsumptionTrend, ~L14764-14844). No llevan prefijo #view-prices
     ni #benchmarkDetailModal a propósito: el modal cuelga fuera de
     #view-prices en el DOM y el otro consumidor las necesita intactas. Se
     resuelven aquí porque el nombre es de este dominio y consumo-
     mermas.css las dejó fuera adrede — candidatas a promover a
     04-componentes si aparece un tercer consumidor (mismo criterio que
     .consumption-unmapped-row en consumo-mermas.css).
   Fuera de alcance: la tabla del benchmark es un <table> plano sin
     geometría propia (.table-wrap + table base de tabla.css/02-base
     alcanzan; v1 no fijaba anchos de columna); el ancho del modal
     (.benchmark-detail-modal, 1240px) ya vive en modal.css. .price-link/
     .price-savings/.price-variation (botón-cifra de la fila, también los
     usa Consumo) no tienen dueño en 04-componentes/06-utilidades — es
     color/tipografía de componente, no geometría de vista: no se resuelven
     aquí (bloquea look final de la tabla, ver duda para P4). La cabecera
     NO fuerza un stack a columna por breakpoint (v1 lo hacía a 1380px):
     se apoya en el flex-wrap de .section-head, igual que .sales-head en
     ventas.css — consistente con esa vista hermana.
   Casos límite: sin insumos con benchmark del filtro → fila vacía (colspan,
     feedback.css); detalle sin marcadores de ahorro → "Sin marcadores..."
     (feedback.css); ahorro $0 no es dato ausente, se pinta igual atenuado
     (.tone-muted, ya resuelto por utilidades).
   Estados: sin estados propios — es geometría/posición; la interacción
     vive en los componentes que envuelve (campo, botón, modal).
   ═══════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Cabecera: título + toolbar de 5 campos (cada uno con hint) puede
     crecer a varias filas — no debe centrar el título contra esa altura. */
  #view-prices .benchmark-head { align-items: flex-start; }

  /* ── Toolbar: grid fino en vez del flex-wrap genérico de toolbar.css.
     Ratios propios porque los 5 campos no pesan igual (buscador ancho,
     período medio, selects angostos) — toolbar.css invita esta afinación
     ("la geometría fina, si una vista la necesita, vuelve desde vistas"). */
  #view-prices .benchmark-tools {
    display: grid;
    grid-template-columns:
      minmax(230px, 1.15fr) minmax(190px, .9fr) minmax(160px, .72fr)
      minmax(150px, .65fr) minmax(170px, .72fr);
    align-items: start;
    gap: 10px;
  }

  /* ── Responsive ────────────────────────────────────────────────────── */
  @media (max-width: 1100px) {
    /* Los anchos fijos ya no caben (riel + padding se comen el sobrante):
       auto-fit reparte los mismos 5 campos en las filas que hagan falta. */
    #view-prices .benchmark-tools { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
  }

  @media (max-width: 680px) {
    #view-prices .benchmark-tools { grid-template-columns: 1fr; }
  }

  /* ── Detalle al clic: gráfico + leyenda + marcadores de ahorro ───────
     Ver nota de cabecera: compartida con #consumptionTrendModal, sin
     prefijo de vista/modal a propósito. */
  .benchmark-detail-context {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 16px;
    margin: 14px 0 12px;
    color: var(--muted);
    font-size: var(--fs-small);
    font-weight: 700;   /* v1: 760 → escala (mismo mapeo que .benchmark-field en campo.css) */
  }
  .benchmark-detail-context span { display: inline-flex; align-items: center; gap: 8px; }   /* v1: 7px → escala */

  /* Muestra de leyenda: pastilla para línea/área, punto para el marcador */
  .benchmark-detail-key { display: inline-block; width: 24px; height: 5px; border-radius: 999px; background: var(--blue); }
  .benchmark-detail-key--price  { background: var(--blue); }
  .benchmark-detail-key--best   { background: var(--green); }
  .benchmark-detail-key--saving { width: 10px; height: 10px; background: var(--red); }

  /* El gráfico vive suelto dentro del modal (sin panel que le dé altura
     resoluble como .report-main-panel): necesita su propio alto explícito.
     Borde/fondo/radio ya los da .report-chart base — no se repiten aquí. */
  .report-chart.benchmark-detail-chart { height: clamp(500px, 56vh, 560px); min-height: 500px; }

  .benchmark-detail-markers { margin-top: 12px; display: grid; gap: 8px; }

  /* Fila-tarjeta de marcador: fecha · proveedor+folio · precio · cantidad ·
     botón de ahorro (.table-action, ya vestido por boton.css). */
  .benchmark-marker-row {
    display: grid;
    grid-template-columns: minmax(120px, .7fr) minmax(180px, 1.2fr) repeat(3, minmax(100px, .6fr));
    gap: 10px;
    align-items: center;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface-2);
  }
  .benchmark-marker-row strong,
  .benchmark-marker-row b { color: var(--ink); }
  .benchmark-marker-row span { color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }

  @media (max-width: 900px) {
    .benchmark-detail-context { align-items: flex-start; }
    .report-chart.benchmark-detail-chart { height: 420px; min-height: 420px; }
    .benchmark-marker-row { grid-template-columns: 1fr; }
  }
}

/* líneas: 125 de presupuesto 150 */
/* ══ vista · consumo y mermas ═════════════════════════════════════════════
   data-view="consumption" — #view-consumption
   Cubre: .consumption-grid (+-table-panel/-side-panel/-table),
     .consumption-unmapped-list/-row, .consumption-advanced-grid,
     .consumption-sales-type, .consumption-basis-note.
   El resto de la vista ya lo resuelven los componentes: KPIs → kpi.css,
     filtros/segmentado/período → toolbar.css, tabla comparativa → skin
     Tabulator (07-terceros). Nada de eso se repite aquí.
   Fuente v1: styles.css ~L1976-2122 (bloque base consumption-*),
     ~L10502-10504 (responsive 980, migrado a 900).
   Casos límite: insumo/categoría largos → ellipsis a una línea en la fila
     de cobertura; "Sin categoría" y datos ausentes los resuelve el JS antes
     de llegar al DOM; sin insumos/sin pendientes → .empty (feedback.css).
   Decisiones de porte:
     · `.consumption-section` pierde el min-height de viewport completo
       (v1: calc(100dvh - var(--nav-alto) - 134px), hack aislado sin receta de componente —
       solo 3 apariciones en todo v1, cada una con un offset distinto). La
       altura real de la vista ya la dan los min-height de los paneles.
     · `.consumption-unmapped-row` es una "tabla div-grid" (PLAN §5, familia
       aún sin construir junto a cobranza/benchmark): se define completa
       aquí porque hoy es su única consumidora. Si otra vista repite el
       patrón exacto, promover a 04-componentes en vez de duplicar.
     · Nota de base de comparación: tinte azul de v1 (rgba a mano, ~5.5%)
       se resuelve con el token `--blue-soft` existente (9%) en vez de
       inventar un tercer tinte — cambio de intensidad apenas perceptible.
   ═══════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Dos columnas: tabla comparativa + cobertura sin receta ──────────── */
  .consumption-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(260px, 320px);
    gap: 16px;
    margin-top: 16px;
  }

  .consumption-table-panel,
  .consumption-side-panel { min-height: 560px; }

  /* El mount de Tabulator (height:'100%' por JS) necesita una altura
     resoluble: la da la fila 1fr de .report-panel apoyada en el min-height
     de arriba — mismo mecanismo que .report-main-panel/-side-panel. */
  .consumption-table { min-height: 480px; height: 100%; }

  /* ── Cobertura: productos vendidos sin receta ─────────────────────────
     Fila = nombre+categoría a la izquierda, monto+cantidad a la derecha.
     b se saca del flujo natural a col2/fila1; span y small completan solos
     por auto-flow (col1/fila2 y col2/fila2). */
  .consumption-unmapped-list {
    display: grid;
    gap: 8px;
    align-content: start;
  }

  .consumption-unmapped-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 2px 10px;
    padding: 10px 0;
    border-bottom: 1px solid var(--line-soft);
  }
  .consumption-unmapped-row:last-child { border-bottom: 0; }
  .consumption-unmapped-row b { grid-column: 2; grid-row: 1; }

  .consumption-unmapped-row strong,
  .consumption-unmapped-row span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;   /* nombre/categoría largos: una línea, sin romper la fila */
  }
  .consumption-unmapped-row strong { color: var(--ink); font-size: var(--fs-body); }
  .consumption-unmapped-row span { color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
  .consumption-unmapped-row b {
    color: var(--ink);
    font-size: var(--fs-small);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
  }
  .consumption-unmapped-row small { color: var(--muted); font-size: var(--fs-caption); text-align: right; }

  /* ── Más filtros: grilla de selects dentro del <details> ──────────────
     toolbar.css ya deja el <details>[open] a flex-basis:100%; aquí solo
     la grilla interna (auto-fit: sin quiebre propio a los 3 breakpoints). */
  .consumption-advanced-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 10px;
    margin-top: 10px;
    padding: 12px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface-2);
  }

  /* Segmentado de tipo de venta: siempre su propia fila al pie del strip */
  .consumption-sales-type { flex-basis: 100%; }

  /* ── Nota de base de comparación: aviso bajo el strip de filtros ──────── */
  .consumption-basis-note {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 8px 14px;
    align-items: baseline;
    margin-top: 14px;
    padding: 12px 14px;
    border: 1px solid color-mix(in srgb, var(--blue) 24%, transparent);
    border-radius: var(--r);
    background: var(--blue-soft);
    color: var(--ink-2);
  }
  .consumption-basis-note strong {
    color: var(--blue);
    font-size: var(--fs-caption);
    letter-spacing: .045em;
    text-transform: uppercase;
  }
  .consumption-basis-note span { font-size: var(--fs-small); line-height: 1.5; }

  /* ── Responsive ────────────────────────────────────────────────────── */
  @media (max-width: 900px) {
    .consumption-grid { grid-template-columns: 1fr; }
    .consumption-table-panel,
    .consumption-side-panel { min-height: 440px; }
  }

  @media (max-width: 680px) {
    .consumption-basis-note { grid-template-columns: 1fr; }
  }
}

/* líneas: 133 de presupuesto 150 */
/* ══ costeo-recetas — #view-recipes (data-view="recipes") ══════════════════
   Cubre: #view-recipes .recipe-head, .recipe-wrap (alto de scroll),
     .recipe-table(+.recipe-admin-table), .item-cell/.recipe-map-select/
     .recipe-ingredient-cell(+small)/.recipe-row-actions (anchos de columna
     de las 3 pestañas: Productos/Subrecetas/Rendimientos comparten tabla y
     wrap), .recipe-yield-form (caja+grilla de alta de rendimiento);
     y en el modal editor de receta (vive fuera de #view-recipes en el DOM,
     por eso sin ese prefijo — ver nota): .recipe-edit-table(+-inner/
     input.numeric), .recipe-edit-catalog, .recipe-price-status,
     .recipe-ingredient-form, .recipe-ingredient-mode-field.
   Fuente v1: styles.css ~L3608-3612+10397-10404 (recipe-head, fusión con
     book-head/sales-head/reports-head/benchmark-head), ~L5148-5162
     (recipe-wrap/recipe-table/recipe-admin-table, fusión con book-wrap/
     sales-wrap), ~L5214+5217 (item-cell), ~L5315 (recipe-map-select),
     ~L5390-5407 (recipe-ingredient-cell/recipe-row-actions), ~L5410-5420+
     10716 (recipe-yield-form), ~L5445-5502+12746-12748 (familia recipe-edit-table
     recipe-ingredient-form — el ancho/alto del PANEL del modal ya vive en
     modal.css, aquí solo su contenido), ~L5674 (recipe-ingredient-mode-
     field) — fusionadas last-wins.
   Nota de anclaje: el modal #recipeEditModal es hermano de las .view en el
     DOM (no hijo de #view-recipes) — sus clases .recipe-edit-* y .recipe-
     ingredient-* son de contenedor propio (prefijo recipe- único, sin otro
     dueño) y se anclan sueltas, como indica CONVENCIONES para ese caso.
   NOTAS-P2 #3 (.recipe-category-toggle): resuelto sin código — la fila de
     categoría completa (colspan="10" + sticky vertical bajo el thead) ya
     vive entera en tabla.css (.recipe-category-row/-toggle); esta vista no
     declara nada sobre esas clases para no pisar esa receta ni duplicarla.
   Gaps detectados, fuera de esta vista (no se resuelven aquí):
     · .recipe-technical-* (ficha técnica: modal + @media print
       body.recipe-tech-printing) es un documento imprimible completo
       (NOTAS-P2 #9), no geometría de esta vista — sigue sin dueño.
   Casos límite: nombre de insumo/ingrediente largo → .recipe-ingredient-
     cell small y .recipe-edit-item-cell small truncan por ellipsis (el
     ancho que dispara el corte es de esta capa; color/tipografía es de
     componente); tabla vacía → colspan de app.js sobre .empty genérico;
     precio o dato ausente → "-" de texto plano ya resuelto por el JS.
   ═══════════════════════════════════════════════════════════════════════ */

@layer vistas {

  .recipe-section { min-width: 0; }
  .recipe-tab-panel[hidden] { display: none; }
  .recipe-summary { margin: 12px 0; }

  /* ── Cabecera: título + 2 segmentados (vista/estado) + buscador + Excel ──
     El bloque de herramientas envuelve a 2 líneas antes de --view-max;
     flex-start evita que el título quede centrado contra ese bloque. */
  #view-recipes .recipe-head { align-items: flex-start; }

  /* ── Scroll propio de la tabla activa: Productos/Subrecetas/Rendimientos
     comparten .recipe-wrap. Se recorta bajo cabecera + resumen KPI en vez
     de estirar el lienzo (estimado, mismo criterio que los demás
     calc(100vh - Npx) fijos del sistema). */
  /* ── Techo de scroll de la tabla ──────────────────────────────────────
     Lo que se resta es lo que hay ENCIMA de la tabla más el aire de abajo:
     padding del workspace + barra de herramientas + cabecera de sección +
     16 de padding de la tarjeta + 28 de respiro. Medido, no estimado — si
     cambia la cabecera hay que volver a medir (el valor viejo venía del
     header antiguo con eyebrow y tarjetas KPI, y reservaba 120-140px que
     ya no existen: esa franja quedaba en blanco bajo la tabla). */
  #view-recipes .recipe-wrap { max-height: calc(100dvh - var(--nav-alto) - 204px); }

  /* ── Ancho de tabla: Productos trae 10 columnas con 2 de texto libre
     (Producto, Receta) — dispara el scroll horizontal de .table-wrap en
     vez de comprimir las columnas de cifras. Subrecetas/Rendimientos
     (recipe-admin-table, 7-8 columnas) necesitan menos piso. */
  #view-recipes .recipe-table { min-width: 1820px; }
  #view-recipes .recipe-admin-table { min-width: 1180px; }
  #view-recipes .item-cell { min-width: 300px; }
  #view-recipes .recipe-map-select { min-width: 320px; }
  #view-recipes .recipe-ingredient-cell { min-width: 190px; }
  #view-recipes .recipe-ingredient-cell small {
    max-width: 230px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  #view-recipes .recipe-row-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 6px;
  }

  /* ── Alta de rendimiento (pestaña Rendimientos): caja propia (sin dueño
     en 04-componentes, exclusiva de esta vista) con 4 campos en fila; el
     insumo se lleva el sobrante, rendimiento queda fijo, los 2 botones se
     ajustan a su contenido. */
  #view-recipes .recipe-yield-form {
    display: grid;
    grid-template-columns: minmax(260px, 1fr) 150px auto auto;
    gap: 10px;
    align-items: end;
    margin: 12px 0;
    padding: 12px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface-2);
  }
  .recipe-yield-source {
    display: block; margin-top: 3px; color: var(--muted);
    font-size: var(--fs-caption); line-height: 1.3;
  }

  /* ── Editor de receta (modal): ancho/alto del panel ya vienen de
     modal.css (.recipe-edit-modal); aquí solo su contenido interno. ──── */

  /* Lista de ingredientes: scroll propio dentro del modal, mismo criterio
     que .table-wrap (tabla.css) pero sin esa clase en el marcado del modal. */
  .recipe-edit-table {
    margin-top: 12px;
    max-height: 54vh;
    overflow: auto;
    border: 1px solid var(--line);
    border-radius: var(--r);
  }
  .recipe-edit-table-inner { min-width: 1220px; }
  /* Cantidad / rendimiento / precio manual: misma columna angosta */
  .recipe-edit-table-inner input.numeric { width: 110px; }
  .recipe-edit-catalog { min-width: 260px; }
  .recipe-edit-item-cell small {
    display: block; max-width: 320px; margin-top: 4px; overflow: hidden;
    color: var(--muted); font-size: var(--fs-caption);
    text-overflow: ellipsis; white-space: nowrap;
  }
  .recipe-price-status { display: block; min-width: 118px; }

  /* Alta de ingrediente: 6 campos, el nombre se lleva 1.5fr del sobrante;
     tipo/cantidad/rendimiento/precio quedan angostos, el botón se ajusta. */
  .recipe-ingredient-form {
    margin-top: 14px;
    display: grid;
    grid-template-columns: 132px minmax(260px, 1.5fr) minmax(112px, .5fr)
      minmax(112px, .5fr) minmax(150px, .65fr) auto;
    gap: 10px;
    align-items: end;
  }
  /* Sin esto el <select> de 132px empuja su columna al min-content del
     texto más largo ("Subreceta") y desborda la grilla fija. */
  .recipe-ingredient-mode-field { min-width: 0; }
  .recipe-delete-db-check { margin-right: auto; color: var(--red); }

  /* Selector de catálogo dentro de los modales de línea/receta. */
  .catalog-field { margin: 12px 0 8px; }
  .catalog-picker-actions { display: flex; justify-content: flex-start; margin-bottom: 8px; }
  .catalog-preview { min-height: 18px; margin: 0 0 12px; color: var(--muted); font-size: var(--fs-caption); }
  .catalog-preview.ok { color: var(--green); }
  .catalog-preview.bad { color: var(--red); }
  .recipe-upload-note { margin-top: 4px; }
  .line-rescan-box {
    display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
    gap: 10px; align-items: end; margin-top: 14px; padding: 12px;
    border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2);
  }
  .line-rescan-box :is(strong, small) { display: block; }
  .line-rescan-box small { margin-top: 3px; color: var(--muted); font-size: var(--fs-caption); }

  /* ── Ficha técnica: documento dentro de modal ─────────────────────── */
  .recipe-technical-body { margin-top: 12px; }
  .recipe-technical-sheet { color: var(--ink); background: var(--surface); }
  .recipe-technical-header {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 230px;
    gap: 20px;
    align-items: stretch;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--line);
  }
  .recipe-technical-header h1 {
    margin: 4px 0 8px;
    font-family: var(--font-display);
    font-size: var(--fs-title);
    font-weight: 400;
    line-height: 1.05;
  }
  .recipe-technical-header p { margin: 0; color: var(--muted); font-weight: 650; }
  .recipe-technical-photo {
    min-height: 170px;
    display: grid;
    place-items: center;
    border: 1px dashed var(--line-2);
    border-radius: var(--r);
    background: var(--surface-2);
    color: var(--muted);
    font-weight: 700;
  }
  .recipe-technical-section,
  .recipe-technical-box {
    margin-top: 14px;
    border: 1px solid var(--line);
    border-radius: var(--r-xs);
    background: var(--surface);
  }
  .recipe-technical-section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--line);
  }
  .recipe-technical-section-head h2,
  .recipe-technical-box h2 { margin: 0; font-size: var(--fs-h3); }
  .recipe-technical-section-head span { padding: 4px 8px; border-radius: 999px; font-size: var(--fs-caption); font-weight: 800; }
  .recipe-technical-section-head span.ok { background: var(--green-soft); color: var(--green); }
  .recipe-technical-section-head span.warn { background: var(--amber-soft); color: var(--amber); }
  .recipe-technical-table { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
  .recipe-technical-table th,
  .recipe-technical-table td { padding: 10px 12px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
  .recipe-technical-table th { color: var(--muted-2); font-size: var(--fs-caption); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
  .recipe-technical-table td :is(strong, small) { display: block; }
  .recipe-technical-table td small { margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); line-height: 1.25; }
  .recipe-technical-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 14px; }
  .recipe-technical-box { padding: 12px 14px; }
  .recipe-technical-box p { min-height: 34px; margin: 8px 0 0; border-bottom: 1px solid var(--line); color: var(--muted); font-weight: 700; }

  /* ── Responsive ──
     900: la cabecera ya apila sola (flex-wrap de section-head/book-tools);
     el scroll de la tabla se recorta un poco más. */
  @media (max-width: 900px) {
    #view-recipes .recipe-wrap { max-height: calc(100dvh - var(--nav-alto) - 354px); }
  }

  /* 680: cada control de la toolbar es su propia fila (toolbar.css) — la
     pila crece bastante más; formularios del modal pasan a 1 columna. */
  @media (max-width: 680px) {
    #view-recipes .recipe-wrap { max-height: calc(100dvh - var(--nav-alto) - 354px); }
    #view-recipes .recipe-yield-form { grid-template-columns: 1fr; }
    .recipe-ingredient-form { grid-template-columns: 1fr; }
    .recipe-edit-table { max-height: 50vh; }
    .line-rescan-box { grid-template-columns: 1fr; }
    .recipe-technical-header,
    .recipe-technical-grid { grid-template-columns: 1fr; }
    .recipe-technical-photo { min-height: 130px; }
  }
}

/* líneas: 139 de presupuesto 150 */
/* ══ cuentas-por-cobrar — data-view="receivables" ═══════════════════════════
   Cubre: #view-receivables .receivables-head (alineación contra un toolbar
          alto), .receivables-tools (ancho para que los 2 segmentados
          full-width de toolbar.css tengan una fila real que llenar),
          .receivables-wrap (alto de scroll), .receivables-table (ancho total
          + .select-col/.supplier-cell), .ar-doc-cell/.ar-invoice-meta y
          .ar-input--wide/--date/--money/--status/--invoice/--boleta/
          --dockind (ancho de columna por campo embebido en celda), columna
          de acciones fija a la derecha (sticky).
   Fuente v1: styles.css ~L3684-3734 (.receivables-section/-head/-tools),
          ~L5152-5235 (.receivables-table/-wrap, .select-col, .supplier-cell,
          sticky última columna), ~L5237-5279 (.ar-input y variantes,
          .ar-doc-cell, .ar-invoice-meta), ~L12767-12781 (@760px, consolidada
          a 680px) — fusionadas last-wins.
   Casos límite: cliente/nota larga → .ar-input--wide (Notas) y .supplier-cell
          (Cliente) dan ancho de sobra antes de comprimir contra las columnas
          de cifra; doc-meta larga ("Número asignado, falta importar Facto")
          → .ar-invoice-meta limita el ancho de línea sin forzar la columna a
          crecer; sin filas o error de carga → colspan=12 que pinta app.js
          sobre .empty (ya vestida en 02-base, sin regla aquí); atraso o
          monto ausente llega como "—"/0 de texto plano desde el JS.
   Deuda no portada: `.receivables-table th:first-child/td:first-child
          {min-width:230px}` de v1 apuntaba al mismo <th> que `.select-col`
          (el checkbox es la 1ª columna real) — `.select-col` la sobreescribe
          con 42px por ir después a igual especificidad, así que ese 230px
          nunca se ve; el 230px vivo es el de Cliente (.supplier-cell, 2ª
          columna), y ese sí se porta. El z-index:2/3 que v1 daba a la
          columna sticky también muere sin reemplazo: un elemento posicionado
          con z-index:auto ya pinta sobre sus hermanos <td> estáticos por
          orden nativo de capas, y sigue por debajo de <th> (z-index:1 de la
          base) — la esquina se resuelve sola, sin números fuera de escala.
   NO cubre — sin anclaje en el DOM actual (verificado con grep: los ids no
          existen en index.php, solo se leen/escriben desde app.js), mismo
          criterio que "sin panel lateral de detalle" en libro-de-compras.css:
          JS huérfano, no se construye geometría para un contenedor que no
          existe. Si se recupera el markup, la geometría vuelve aquí:
          · Tira KPI (#arOutstanding/#arTotal/#arCustomers/#arPendingCount/
            #arAvgDelay/#arMaxDelay, 6 métricas) — candidata natural a
            reusar `.sales-summary`, ya cubierta entera por kpi.css.
          · Antigüedad de saldos (#arAgingBars, .ar-aging-bars/-row) y
            ranking de clientes (#arCustomerList, .ar-customer-list/-row).
          · Formulario de reglas de cobranza (#arRuleType/#arRuleValue/
            #arRuleOptions, .receivables-rule-form/-list, .ar-rule-chip/
            -empty).
          `.ar-input` y `.row-check` viven en campo.css; esta vista solo fija
          el ancho de cada variante según su columna.
   ═══════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Título arriba a la izquierda: la toolbar trae 2 segmentados a fila
     completa + buscador/período/2 botones — un bloque alto que no debe
     centrar el título contra su propio alto (receta base de section-head
     es align-items:center). */
  #view-receivables .receivables-head { align-items: flex-start; }

  /* Los segmentados de toolbar.css llevan flex-basis:100% *dentro* de este
     contenedor: sin ancho propio, ese 100% queda relativo a un flex item
     angosto (shrink-to-fit). Forzar el ancho completo es lo que les da una
     fila real que llenar. */
  #view-receivables .receivables-tools { width: 100%; }

  /* Alto de scroll: resta header de vista + cabecera + las ~3 filas del
     toolbar alto (medido en v1, constante — este toolbar es alto siempre,
     no solo en breakpoints angostos). */
  /* ── Techo de scroll de la tabla ──────────────────────────────────────
     Lo que se resta es lo que hay ENCIMA de la tabla más el aire de abajo:
     padding del workspace + barra de herramientas + cabecera de sección +
     16 de padding de la tarjeta + 28 de respiro. Medido, no estimado — si
     cambia la cabecera hay que volver a medir (el valor viejo venía del
     header antiguo con eyebrow y tarjetas KPI, y reservaba 120-140px que
     ya no existen: esa franja quedaba en blanco bajo la tabla). */
  #view-receivables .receivables-wrap { max-height: calc(100dvh - var(--nav-alto) - 236px); }

  @media (max-width: 900px) {
    #view-receivables .receivables-wrap { max-height: calc(100dvh - var(--nav-alto) - 304px); }
  }

  /* 12 columnas, la mayoría con un input embebido: sin ancho mínimo total
     el navegador las aprieta antes de ofrecer scroll horizontal. */
  #view-receivables .receivables-table { min-width: 1360px; }

  /* Checkbox de selección: columna angosta fija, no cifra ni texto. */
  #view-receivables .receivables-table .select-col {
    width: 42px; min-width: 42px; max-width: 42px;
    text-align: center;
  }

  /* Cliente: única columna de texto libre larga (razón social + RUT). */
  #view-receivables .receivables-table .supplier-cell { min-width: 230px; }

  /* Documento: agrupa select de tipo + input factura/boleta + meta — el
     ancho fuerza el wrap natural entre esos controles dentro de la celda. */
  #view-receivables .ar-doc-cell { min-width: 168px; }
  #view-receivables .ar-invoice-meta { max-width: 190px; }

  /* Ancho por variante de campo embebido en celda (Fecha/Monto/Estado/
     Factura/Boleta/Tipo doc/Notas) — geometría de columna, no de control. */
  #view-receivables .ar-input--wide    { min-width: 170px; }
  #view-receivables .ar-input--date    { min-width: 132px; }
  #view-receivables .ar-input--money   { min-width: 112px; text-align: right; }
  #view-receivables .ar-input--status  { min-width: 108px; }
  #view-receivables .ar-input--invoice { min-width: 148px; }
  #view-receivables .ar-input--boleta  { min-width: 148px; }
  #view-receivables .ar-input--dockind {
    min-width: 92px;
    margin-bottom: 4px;
    font-weight: 650;
  }

  /* Última columna (acción "Guardar") fija a la derecha: único caso de
     sticky horizontal de esta tabla. Sin z-index — un elemento posicionado
     ya pinta sobre sus hermanos <td> estáticos por orden nativo de capas
     (ver Deuda no portada, arriba). */
  #view-receivables .receivables-table th:last-child,
  #view-receivables .receivables-table td:last-child {
    position: sticky;
    right: 0;
    background: var(--surface);
    /* Filo + sombra suave: con solo la línea de 1px, el texto de la columna
       vecina se deslizaba por debajo y parecía cortado a media palabra en
       vez de "hay más tabla ahí". */
    box-shadow: -1px 0 0 var(--line), -10px 0 14px -10px color-mix(in srgb, var(--ink) 22%, transparent);
  }
  #view-receivables .receivables-table tbody tr:hover td:last-child {
    background: var(--surface-2);
  }

  /* ── Panel a sangre ──────────────────────────────────────────────────────
     El markup es `.table-section.receivables-section`, y la cabecera de esta
     vista trae su propio padding: sin este reset se sumaría al de
     .table-section (panel.css) y se vería doble marco. */
  .receivables-section {
    --pad-tarjeta: 0;
    overflow: hidden;
  }

  /* ── Antigüedad de la deuda (aging) ──────────────────────────────────────
     Cada tramo es una fila: rótulo · barra proporcional · monto. La barra es
     un riel fijo (i) con relleno variable (b) cuyo ancho lo calcula el JS. */
  #view-receivables .ar-aging-row {
    display: grid;
    grid-template-columns: 72px minmax(90px, 1fr) 124px;
    gap: 10px;
    align-items: center;
    min-height: 30px;
  }

  #view-receivables .ar-aging-row span {
    color: var(--muted);
    font-size: var(--fs-small);
    font-weight: 650;
  }

  #view-receivables .ar-aging-row i,
  #view-receivables .ar-customer-row i {
    display: block;
    height: 7px;
    overflow: hidden;
    border-radius: 999px;
    background: var(--surface-3);
  }

  #view-receivables .ar-aging-row i b,
  #view-receivables .ar-customer-row i {
    display: block;
    height: 100%;
    border-radius: inherit;
  }

  /* Escala de mora: al día → vencida. El tramo naranja se deriva mezclando
     ámbar y rojo (v1 lo traía como hex suelto #c0642a). */
  .ar-aging-row--green  i b { background: var(--green); }
  .ar-aging-row--blue   i b { background: var(--blue); }
  .ar-aging-row--amber  i b { background: var(--amber); }
  .ar-aging-row--orange i b { background: color-mix(in srgb, var(--amber) 55%, var(--red)); }
  .ar-aging-row--red    i b { background: var(--red); }

  /* ── Fila-cliente (botón que abre el detalle) ───────────────────────────
     Es un <button>: la elipsis en sus tres textos evita que un nombre largo
     empuje el monto fuera de la fila. */
  #view-receivables .ar-customer-row {
    width: 100%;
    min-height: 58px;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 12px;
    align-items: center;
    padding: 8px 10px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
    color: var(--ink);
    text-align: left;
  }

  #view-receivables .ar-customer-row:hover {
    background: var(--surface-2);
    border-color: var(--line-2);
  }

  #view-receivables .ar-customer-row span,
  #view-receivables .ar-customer-row strong,
  #view-receivables .ar-customer-row small {
    min-width: 0;
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  #view-receivables .ar-customer-row strong {
    font-size: var(--fs-small);
    font-weight: 800;
  }

  /* Sin reglas de cobranza configuradas */
  #view-receivables .ar-rule-empty {
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 650;
  }

  /* 680px: la toolbar ya apiló cada control a su propia fila (toolbar.css);
     el scroll interno de la tabla deja de tener sentido sobre esa altura. */
  @media (max-width: 680px) {
    #view-receivables .receivables-wrap { max-height: none; }
  }
}

/* líneas: 208 de presupuesto 150 (excedido: la vista absorbió el aging y la
   fila-cliente, que en v1 vivían sueltos y no tenían componente propio) */
/* ══ cuentas-por-pagar — data-view="payables" ═══════════════════════════════
   Cubre: #view-payables .section-head, #view-payables .accounting-table-wrap,
     #view-payables .accounting-table (ancho mínimo + alineación de columnas
     numéricas; el resto del look de tabla ya lo da la receta canon de
     02-base — ver Nota).
   Fuente v1: styles.css ~L15836 (.financial-operations-head, align-items),
     ~L9372 (.flow-head, margin-bottom — ambas clases conviven en el mismo
     .section-head de esta vista), ~L15930-15937 (.accounting-table-wrap/
     -table: ancho y alineación; NO se porta la fórmula de alineación, ver
     Nota).
   Casos límite: sin movimientos de proveedores en el periodo → fila única
     con colspan que pinta app.js sobre .empty (ya vestida en 02-base, sin
     regla aquí); pago o factura ausente en la fila → "—" de texto plano
     desde el JS, no CSS; memo/detalle largo → envuelve dentro de la celda
     (v1 no truncaba esta tabla, a diferencia de otras).
   Nota — fórmula de alineación corregida: v1 alineaba a la derecha con
     `td:nth-last-child(-n+4)`, pensada para una tabla con más columnas;
     sobre las 6 de este mayor auxiliar (Fecha/Asiento/Detalle/Pagos/
     Facturas/Saldo) esa fórmula también atrapaba "Detalle" (texto libre).
     Se corrige aquí a las 3 columnas de cifra reales — es deuda de v1, no
     intención a preservar (CONVENCIONES §"Cómo portar desde v1").
   NO cubre — gap fuera de este archivo, verificado sin regla en ningún
     04-componentes hoy (2026-08-09): `.accounting-kpi-grid` (tarjetas KPI,
     familia idéntica a `.annex-kpi-grid`/`.coffee-quote-kpis` → falta
     sumarla al `:is()` de kpi.css), `.accounting-panel` (superficie tarjeta,
     misma receta que `.table-section`/`.report-panel` → falta sumarla a
     panel.css), `.accounting-basis-banner` y `.finance-status-badge`
     (banner + pill de estado del periodo, usados también por Libros/
     Situación financiera/Cuenta del propietario) y `.accounting-entry-link`
     (link de asiento, familia de `.account-link`). Ninguno se resuelve aquí
     por ser skin de componente, no geometría de esta vista (CONVENCIONES
     §8); afecta por igual a las vistas hermanas (cobrar, libros, cuenta del
     propietario, situación financiera) — no es exclusivo de pagar.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Título arriba, selector de periodo abajo a la derecha: v1 fusiona
     .financial-operations-head + .flow-head sobre el mismo .section-head
     (que por sí solo, desde panel.css, centra verticalmente). */
  #view-payables .section-head {
    align-items: flex-end;
    margin-bottom: 14px;
  }

  /* 6 columnas con dos de texto libre (Asiento, Detalle); sin ancho mínimo
     el navegador las aprieta antes de ofrecer scroll horizontal — el
     min-width es lo que dispara ese scroll dentro de .accounting-table-wrap. */
  #view-payables .accounting-table-wrap { overflow-x: auto; }
  #view-payables .accounting-table { min-width: 850px; }

  /* Solo las 3 columnas de cifra (Pagos/debe, Facturas/haber, Saldo) van a
     la derecha; tabular-nums ya lo da la base para todo td/th. */
  #view-payables .accounting-table th:nth-child(n+4),
  #view-payables .accounting-table td:nth-child(n+4) {
    text-align: right;
  }
}

/* líneas: ~59 de presupuesto 150 */
/* ══ estado-resultados — data-view="flow" ═══════════════════════════════════
   Cubre: #view-flow .section-head (solo margin-bottom), .flow-table (ancho
     de columnas — NOTAS-P2 #4), familias .flow-val--*, .flow-zero,
     .flow-month--current y
     .flow-nav-btn (NOTAS-P2 #2), .flow-section-hint, familia de edición
     inline (.flow-cell-edit-group/-form/-input/-save-btn/-cancel-btn,
     .flow-edit-btn, .flow-editable-row), familia de sub-filas de nómina
     (.flow-payroll-detail-row/-cost-row/-reconcile-row, .flow-payroll-source
     +.is-manual), y la tabla del modo "Mes seleccionado" (.finance-statement-
     wrap/-table/-section, .finance-variance, .finance-support-grid).
   Fuente v1: styles.css ~L9372-9852 (familias .flow-*, .finance-statement-* y
     .finance-support-grid completa, fusión last-wins de la única capa que
     las trae) — ~L9586-9591 sólo confirma que #view-flow es full-width
     (redundante con .view de 03-layout, no se porta).
   Decisiones NOTAS-P2:
     #1 `.flow-head .icon-button` (v1 20px/36×36): NO se reintroduce. `.flow-
        head` recae en 10 vistas contables (grep index.php); en las 5 que
        envuelven su nav en `.header-period-nav` (flow, cashflow, financial-
        close, bank-statements, fixed-assets) el botón ya queda en 32×32 vía
        page-header.css (P2) — ese tamaño reemplaza al 36×36 de v1 por
        decisión de unificación. balance.css/cuentas-por-pagar.css (mismas
        vistas hermanas, book-tools sin header-period-nav) tampoco lo
        reintrodujeron: mismo criterio aplicado aquí para no crear una
        tercera talla ad-hoc.
     #2 `.flow-val--*`/`.flow-zero`/`.flow-month--current`/`.flow-nav-btn`:
        SÍ viven aquí. Verificado por grep en app.js: sólo renderFlow()/
        renderFlowStatement() los emiten. tabla.css los excluyó a propósito
        ("quedan para utilidades/botón"), pero no son paleta genérica —
        verde=positivo, rojo=sobre meta food cost, ámbar=IVA a favor son
        lecturas propias de este P&L, no reusables por otra vista.
     #4 `.flow-table` primera columna: 176px mínimo (con white-space:nowrap
        de tabla.css el texto largo la sigue empujando más ancha).
   NO cubre — gap de componente compartido, no de esta vista (mismo criterio
     que cuentas-por-pagar.css §NOTAS-P2 #11; nuevo hallazgo, no listado aún
     en NOTAS-P2.md — se agrega ahí):
     · `.finance-period-banner`(+`-main`)/`.finance-source-coverage`: emitidas
       por financeStatusBannerMarkup(), reutilizada tal cual por Resumen
       financiero (`resumen-financiero.css` ya declina resolverla citando
       "Estado de resultados (flow, misma función)"). Ninguna de las dos
       vistas la resuelve por decreto propio — candidato: panel.css/kpi.css.
     · `.statement-account-link`: comparte receta con `.account-link`/
       `.accounting-entry-link` (familia "libro-mayor" de NOTAS-P2 #11) —
       se resuelve junto a esas, en boton.css.
   Fuera de alcance, verificado MUERTO: `.flow-conventions` (styles.css
     ~L14037) no tiene emisor en app.js/index.php — no se porta.
   Nota de propiedad: la familia de edición inline y las sub-filas de nómina
     no son instancia de ningún componente de 04-componentes (verificado en
     los "Cubre" de tabla/botón/campo) y son exclusivas de esta vista — esta
     vista fija también su identidad mínima, mismo criterio que balance.css
     con `.balance-sheet-row`. `.finance-statement-*`/`.finance-variance`/
     `.finance-support-grid` son, igual, exclusivas de este render (grep
     app.js) y es literalmente la tabla que da nombre a la vista.
   Estados: `.flow-nav-btn` — reposo/hover (color+underline); focus-visible
     lo hereda del reset de botón en 02-base; sin active/disabled (value=0
     nunca se emite como botón, ver flowStatementAmount). Edición inline:
     reposo → `.flow-edit-btn` visible al hover de `.flow-editable-row` →
     formulario con guardar/cancelar; sin loading propio (guarda optimista,
     re-renderiza) ni error propio (usa el aviso genérico de la app).
   Casos límite: valor 0 → "—" (.flow-zero); mes actual sin datos igual pinta
     `.flow-month--current`; cuenta contable ausente → fila sin botón de
     navegación (nav=null); nombre de fila largo con nowrap del componente
     no se corta (columna crece, scroll horizontal del wrap se encarga).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Selector de mes + tabs de modo empujan el título: mismo respiro que v1
     daba a `.flow-head` (no combina con `.financial-operations-head` aquí,
     a diferencia de balance-sheet/payables, así que no hereda su align-items). */
  #view-flow .section-head { margin-bottom: 14px; }

  /* ── Matriz de tendencia anual (modo "Tendencia anual", .flow-table) ──
     13 columnas (etiqueta + 12 meses): tabla.css deja el ancho a propósito
     (NOTAS-P2 #4) porque depende del contenido de ESTA tabla, no es genérico. */
  .flow-table { min-width: 860px; }
  .flow-table th:first-child,
  .flow-table td:first-child { width: 176px; min-width: 176px; }

  /* Columna del mes en curso: al vivir en @layer vistas gana sin !important
     a los fondos de fila de tabla.css (subtotal/total/meta), pese a que
     v1 necesitaba !important por estar todo en una capa plana. */
  .flow-table td.flow-month--current { background: color-mix(in srgb, var(--green) 5%, var(--surface)); }
  .flow-table thead th.flow-month--current { color: var(--green); font-weight: 800; }

  .flow-section-hint { font-weight: 550; text-transform: none; letter-spacing: 0; opacity: .65; }

  /* Coloreado semántico de cifra (NOTAS-P2 #2) */
  .flow-val--green { color: var(--green); font-weight: 700; }
  .flow-val--red    { color: var(--red);   font-weight: 700; }
  .flow-val--amber  { color: var(--amber); font-weight: 700; }
  .flow-val--muted  { color: var(--muted); font-weight: 400; }
  .flow-zero        { color: var(--muted-2); font-weight: 400; }

  /* Celda-botón: navega al libro/módulo de respaldo del valor (NOTAS-P2 #2) */
  .flow-nav-btn {
    display: block; width: calc(100% + 8px); margin: -2px -4px; padding: 2px 4px;
    border: 0; border-radius: var(--r-xs);
    background: none; color: inherit; font: inherit; text-align: right;
    cursor: pointer;
    transition: color var(--t);
  }
  .flow-nav-btn:hover { color: var(--blue); text-decoration: underline; }

  /* ── Edición inline: "Otros egresos" y costo manual de remuneraciones ──
     Sin componente dueño (ver Nota de propiedad): esta vista fija su look. */
  .flow-cell-edit-group,
  .flow-cell-form { display: inline-flex; align-items: center; justify-content: flex-end; gap: 4px; }
  .flow-edit-btn {
    padding: 2px 4px; border: 0; border-radius: var(--r-xs);
    background: none; color: var(--muted-2); font-size: var(--fs-caption); line-height: 1;
    opacity: .4; cursor: pointer;
    transition: color var(--t), background var(--t), opacity var(--t);
  }
  .flow-editable-row:hover .flow-edit-btn { opacity: 1; }
  .flow-edit-btn:hover { color: var(--blue); background: var(--blue-soft); opacity: 1; }
  .flow-cell-input { width: 84px; height: 26px; padding: 0 6px; font-size: var(--fs-small); text-align: right; border-radius: var(--r-xs); }
  .flow-cell-save-btn,
  .flow-cell-cancel-btn {
    width: 26px; height: 26px; padding: 0;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid var(--line-2); border-radius: var(--r-xs);
    background: var(--surface); font-size: var(--fs-small); font-weight: 700;
    cursor: pointer;
    transition: background var(--t), color var(--t);
  }
  .flow-cell-save-btn { color: var(--green); border-color: color-mix(in srgb, var(--green) 24%, transparent); }
  .flow-cell-save-btn:hover { background: var(--green-soft); }
  .flow-cell-cancel-btn { color: var(--muted); }
  .flow-cell-cancel-btn:hover { background: var(--surface-2); }

  /* ── Sub-filas de nómina (detalle/costo empresa/reconciliación) ────────
     Mismo criterio: sin dueño arriba, geometría e identidad propias. */
  .flow-payroll-detail-row td:first-child { padding-left: 20px; }
  .flow-payroll-cost-row td {
    background: color-mix(in srgb, var(--green) 6%, var(--surface));
    border-top: 1px solid color-mix(in srgb, var(--green) 20%, transparent);
    font-weight: 700;
  }
  .flow-payroll-cost-row td:first-child { color: var(--green); font-weight: 800; }
  /* Reconciliación: misma receta que .flow-row--meta (tabla.css) sobre otro
     selector — el DOM no puede llevar ambas clases, se repite la fórmula. */
  .flow-payroll-reconcile-row td { background: var(--surface-2); color: var(--muted); font-size: var(--fs-caption); padding-block: 4px; }
  .flow-payroll-reconcile-row td:first-child { padding-left: 20px; font-style: italic; }
  .flow-payroll-source { padding: 0; border: 0; background: none; color: var(--blue); font: inherit; white-space: nowrap; cursor: pointer; }
  .flow-payroll-source:hover { text-decoration: underline; }
  .flow-payroll-source.is-manual { color: var(--muted); cursor: default; }

  /* ── Modo "Mes seleccionado": tabla P&L de 6 columnas + 5 indicadores ──
     Exclusivas de este render (grep app.js) — es la tabla que da nombre a
     la vista, por eso la vista fija también su identidad, no sólo su grilla. */
  .finance-statement-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
  .finance-statement-table { width: 100%; min-width: 900px; table-layout: fixed; border-collapse: collapse; font-size: var(--fs-body); }
  .finance-statement-table th,
  .finance-statement-table td { padding: 10px 12px; border-bottom: 1px solid var(--line); text-align: right; vertical-align: middle; }
  .finance-statement-table th {
    background: var(--surface-2); color: var(--muted-2);
    font-size: var(--fs-caption); font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  }
  .finance-statement-table th:first-child,
  .finance-statement-table td:first-child { width: 30%; text-align: left; }
  .finance-statement-table td:first-child { color: var(--ink-2); font-weight: 550; }
  .finance-statement-table td:first-child small { display: block; margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); font-weight: 400; }
  .finance-statement-section td {
    padding: 8px 12px; background: var(--surface-3); color: var(--ink-2);
    font-size: var(--fs-caption); font-weight: 800; letter-spacing: .085em; text-transform: uppercase;
    border-block: 1px solid var(--line-2);
  }
  .finance-statement-table tr.is-muted td { color: var(--muted); }
  .finance-statement-table tr.is-subtotal td { background: var(--surface-2); font-weight: 700; }
  .finance-statement-table tr.is-total td {
    background: color-mix(in srgb, var(--green) 6%, var(--surface));
    border-block: 1px solid color-mix(in srgb, var(--green) 24%, transparent);
    color: var(--ink); font-weight: 800;
  }

  .finance-variance { display: inline-flex; align-items: flex-end; justify-content: flex-end; gap: 4px; }
  .finance-variance small { color: var(--muted); font-size: var(--fs-caption); }
  .finance-variance.is-up { color: var(--blue); }
  .finance-variance.is-down { color: var(--amber); }

  .finance-support-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-top: 16px; }
  .finance-support-grid > article { min-width: 0; padding: 16px; display: grid; gap: 4px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); }
  .finance-support-grid small { color: var(--muted); font-size: var(--fs-caption); font-weight: 800; letter-spacing: .055em; text-transform: uppercase; }
  .finance-support-grid strong { color: var(--ink); font-size: var(--fs-h2); font-variant-numeric: tabular-nums; }
  .finance-support-grid span { color: var(--muted); font-size: var(--fs-caption); line-height: 1.35; }
  .finance-support-grid .is-tax { background: color-mix(in srgb, var(--blue) 9%, var(--surface)); border-color: color-mix(in srgb, var(--blue) 16%, transparent); }

  @media (max-width: 900px) {
    .finance-support-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  @media (max-width: 680px) {
    .finance-support-grid { grid-template-columns: 1fr; }
  }
}

/* líneas: 200 de presupuesto 150 (+50: dos tablas completas sin dueño
   arriba —P&L del mes y tendencia anual de 13 columnas— más edición inline
   y sub-filas de nómina, ninguna con componente que las cubra; cabecera
   extensa por documentar 3 decisiones NOTAS-P2 + 2 gaps de componente
   compartido recién hallados. Ver Nota de propiedad, mismo criterio que
   balance.css) */
/* ══ eventos — #view-coffee (data-view="coffee") ═══════════════════════════
   Cubre: .event-hub(+-steps oculto/-actions), .split-grid.coffee-grid,
     .coffee-form-grid, .coffee-event-product-picker(+-modes/-add(+[panel=
     catalog])/-list/-row/-total), .coffee-quote-section(+-request/-prompt/
     -params/-toggle-row/-document/-title/-total/-actions/-summary/-add
     (+-head/-modes/-fields(+--free))/-table-wrap/-table/-qty/-row-actions/
     -notes), .coffee-catalog-form(+-description-field/-form-actions/
     -admin-wrap), .coffee-catalog-table .table-inline-field,
     .catalog-live-grid/-controls/-preview, .catalog-ctrl-block(+-body),
     .catalog-photo-previews(+-chip), .catalog-products-head,
     .catalog-product-groups/-group-items/-item-row,
     .coffee-dashboard-section, .coffee-company-dashboard(+-card(+--ok/
     --watch/--stale)/-card-head/-metrics)
   Fuente v1: styles.css ~L7087-7260 (hub+ficha+picker), ~L7290-7380
     (modal catálogo admin), ~L7462-7830 (cotizador), ~L7829-7960
     (dashboard empresas), ~L13701 (KPI del hub → page-header, v117),
     ~L18415-18790 (catálogo v2: editor con vista previa en vivo) —
     fusión last-wins.
   NO cubre — ya resuelto en 04-componentes, no se re-skinea aquí:
     .tool-panel/.button-row/.form-grid/.stack-form/.check-line (campo.css/
     gap sin dueño, ver abajo); .run-list/.coffee-quote-kpis/.coffee-catalog-
     summary (grid + tarjeta completas en kpi.css, auto-fit — no se fuerza
     un conteo de columnas fijo); .coffee-event-status/.coffee-company-status/
     .coffee-status-light (badge.css); .modal-panel/.coffee-catalog-modal/
     .catalog-export-modal (ancho/alto/scroll en modal.css — aquí solo su
     interior); .table-wrap (receta base en tabla.css, esta vista solo pone
     techos de alto donde aplica).
   Gaps detectados, fuera de esta vista (no se resuelven aquí):
     · .tool-panel — superficie de "Ficha evento"/"Resumen" (border/bg/
       padding) no tiene receta en panel.css; se repite en ≥8 vistas más
       (pedidos, reconciliación, daily, RRHH…) — falta subirla ahí, no es
       geometría de esta vista. Solo se fija su alto mínimo en el panel
       lateral compacto (dato que sí es de esta vista).
     · .coffee-event-product-modes / .coffee-quote-add-modes (+ sus button/
       .active) duplican el patrón de .segmented-control con clase propia;
       toolbar.css ya alía otras pestañas bespoke (.orders-tabs,
       .accounting-tabs…) a esa receta pero no estas dos — falta sumarlas
       ahí. No se reimplementa el mismo skin aquí para no multiplicar la
       receta (regla de oro: nunca dos definiciones de la misma pastilla).
     · familia .coffee-quote-type--* y .coffee-quote-tags — badges (mismo
       patrón --badge-c que .pill) sin entrada en badge.css.
     · .coffee-quote-check compone .check-line (campo.css, ya resuelto) +
       una variante visual propia (pastilla) que el DOM no nombra aparte;
       correspondería un .check-line--pill nombrado en campo.css.
     · Editor de foto del catálogo (.catalog-photo-editor/-stage/-zoom/
       -actions, recorte con drag/zoom) es un widget propio de cropping,
       no geometría de esta vista — fuera de alcance.
   Casos límite: sin productos/líneas → listas vacías, sin fila fantasma
     (el vacío lo pinta feedback.css/JS); nombre de producto o empresa largo
     → ellipsis; food cost sin datos → "—" de texto plano (JS); tabla de
     cotización ancha → scroll propio vía .coffee-quote-table-wrap + min-
     width, no estira el modal.
   Cambios visibles: tamaños/pesos fuera de escala llevados al token más
     cercano (10→11, 12.5→12, 14→14.5, 19/22→21); gaps de 7/9px → 8 (escala
     de espaciado); fondo #e9e6df de la vista previa en vivo → var(--surface-
     3) (tono frío del canon en vez del cálido "papel" de v1).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Hub: título + acciones. Los KPI (.event-hub-steps) alimentan al
     page-header (app.js lee estos mismos <strong id="eventHub*">) — se
     ocultan aquí, no se borran: siguen siendo la fuente del dato. */
  .event-hub { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px; margin-bottom: 12px; }
  .event-hub-steps { display: none; }
  .event-hub-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }

  /* ── Ficha evento + resumen lateral ───────────────────────────────────── */
  .coffee-grid { align-items: stretch; }
  .coffee-grid .tool-panel.compact { min-height: 160px; }   /* no queda enano junto al form de 4 columnas */
  .coffee-form-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }

  /* ── Selector de producto: tabs catálogo/listo/preparado ────────────────
     Cajón propio dentro del form; cada modo cambia cuántos campos trae la
     fila de alta (catálogo no lleva costo unitario, preparado sí receta). */
  .coffee-event-product-picker {
    display: grid; gap: 8px;
    padding: 10px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface-2);
  }
  .coffee-event-product-modes { display: inline-grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 4px; width: min(520px, 100%); }
  .coffee-event-product-add {
    display: grid;
    grid-template-columns: minmax(220px, 1fr) 110px minmax(150px, .45fr) auto;
    gap: 8px;
    align-items: end;
  }
  .coffee-event-product-add[data-coffee-product-panel="catalog"] { grid-template-columns: minmax(220px, 1fr) 110px auto; }
  .coffee-event-product-list { display: grid; gap: 8px; }
  .coffee-event-products-empty { color: var(--muted); font-size: var(--fs-small); font-weight: 650; }
  .coffee-event-product-row {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center;
    padding: 8px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
  }
  .coffee-event-product-row strong { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-body); font-weight: 700; }
  .coffee-event-product-row small { display: block; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); font-size: var(--fs-caption); }
  .coffee-event-product-total {
    display: flex; justify-content: space-between; gap: 12px;
    padding: 8px;
    border-radius: var(--r-sm);
    color: var(--muted); font-size: var(--fs-small); font-weight: 700;
  }
  .coffee-event-product-total strong { color: var(--ink); }

  /* ── Cotizador: requerimiento + parámetros, luego documento generado ───── */
  .coffee-quote-request { display: grid; grid-template-columns: minmax(300px, 1fr) minmax(340px, 1.1fr); gap: 16px 20px; align-items: stretch; }
  .coffee-quote-section { margin-top: 14px; }
  .coffee-quote-result { min-width: 0; margin-top: 16px; }
  .coffee-quote-prompt { display: flex; flex-direction: column; }
  .coffee-quote-prompt textarea { flex: 1; min-height: 128px; }
  .coffee-quote-params { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px 12px; align-content: start; }
  .coffee-quote-toggle-row { display: flex; flex-wrap: wrap; align-items: end; gap: 8px 14px; min-height: 38px; }

  .coffee-quote-document { display: grid; gap: 14px; }
  .coffee-quote-title { display: grid; grid-template-columns: minmax(0, 1fr) minmax(180px, auto); gap: 14px; align-items: start; border-bottom: 1px solid var(--line); padding-bottom: 12px; }
  .coffee-quote-title h3 { margin: 0; font-size: var(--fs-h2); }   /* peso hereda de la h3 base (700); v1 solo agrandaba el tamaño */
  .coffee-quote-title small,
  .coffee-quote-summary p { display: block; margin-top: 4px; color: var(--muted); font-size: var(--fs-small); line-height: 1.35; }
  .coffee-quote-total { justify-self: end; min-width: 180px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); text-align: right; }
  .coffee-quote-total span { display: block; color: var(--muted); font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; }
  .coffee-quote-total strong { display: block; margin-top: 4px; font-size: var(--fs-h2); }
  .coffee-quote-actions { display: flex; justify-content: flex-end; margin-top: 8px; }
  .coffee-quote-summary { display: grid; gap: 6px; }

  /* Alta de línea: modo catálogo/libre cambia el número de campos */
  .coffee-quote-add { display: grid; gap: 10px; padding: 12px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .coffee-quote-add-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 14px; }
  .coffee-quote-add-head small { display: block; margin-top: 4px; color: var(--muted); font-size: var(--fs-caption); }
  .coffee-quote-add-modes { display: inline-grid; grid-auto-flow: column; grid-auto-columns: minmax(96px, auto); gap: 4px; }
  .coffee-quote-add-fields { display: grid; grid-template-columns: minmax(220px, 1.4fr) 110px auto; gap: 8px; align-items: end; }
  .coffee-quote-add-fields--free { grid-template-columns: minmax(200px, 1.4fr) minmax(120px, .6fr) 100px minmax(130px, .7fr) auto; }

  /* Tabla de líneas: scroll propio, no estira el modal */
  .coffee-quote-table-wrap { max-height: 720px; }
  .coffee-quote-table { min-width: 680px; }
  .coffee-quote-table th, .coffee-quote-table td { vertical-align: top; }
  .coffee-quote-table td:first-child { min-width: 220px; }
  .coffee-quote-qty { display: inline-grid; grid-template-columns: 76px auto; gap: 6px; align-items: center; justify-content: end; }
  .coffee-quote-row-actions { display: flex; flex-wrap: wrap; gap: 6px; }
  .coffee-quote-notes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .coffee-quote-notes div { padding: 10px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .coffee-quote-notes strong { display: block; margin-bottom: 6px; font-size: var(--fs-small); }
  .coffee-quote-notes span { display: block; color: var(--muted); font-size: var(--fs-small); line-height: 1.35; }

  /* ── Modal Catálogo (administración CRUD, #coffeeCatalogModal) ─────────── */
  .coffee-catalog-form {
    display: grid;
    grid-template-columns: minmax(180px, 1.35fr) minmax(150px, 1fr) minmax(120px, .8fr) minmax(110px, .65fr) minmax(120px, .75fr) auto;
    gap: 8px; align-items: end; margin-bottom: 12px;
  }
  .coffee-catalog-description-field { grid-column: span 2; }
  .coffee-catalog-form-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; }
  .coffee-catalog-admin-wrap { max-height: min(52vh, 560px); }
  .coffee-catalog-table .table-inline-field { width: min(260px, 100%); }
  .coffee-catalog-active-check {
    min-height: var(--control-h); display: flex; align-items: center;
    gap: 8px; color: var(--ink-2); font-size: var(--fs-small); font-weight: 700;
  }
  .coffee-catalog-active-check input { width: auto; height: auto; }
  .calendar-event-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  /* ── Catálogo exportable: columna de control (scroll propio) + vista
     previa en vivo (iframe a escala real, absorbe el resto del ancho) ───── */
  .catalog-live-grid { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(340px, 420px) minmax(0, 1fr); gap: 16px; margin-top: 8px; }
  .catalog-live-controls { min-height: 0; overflow-y: auto; display: grid; gap: 12px; align-content: start; padding-right: 6px; }
  .catalog-ctrl-block { border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); }
  .catalog-ctrl-block summary { padding: 10px 12px; cursor: pointer; font-size: var(--fs-body); font-weight: 700; color: var(--ink-2); }
  .catalog-ctrl-body { display: grid; gap: 10px; padding: 2px 12px 12px; border-top: 1px solid var(--line); }
  .catalog-photo-previews { display: flex; flex-wrap: wrap; gap: 8px; }
  .catalog-photo-chip { position: relative; width: 84px; height: 60px; border: 1px solid var(--line); border-radius: var(--r-xs); overflow: hidden; background: var(--surface-2); }
  .catalog-photo-chip img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .catalog-photo-chip button {
    position: absolute; top: 4px; right: 4px;
    width: 20px; height: 20px; padding: 0; border: 0; border-radius: 50%;
    background: color-mix(in srgb, var(--ink) 82%, transparent);
    color: var(--surface); font-size: var(--fs-caption); cursor: pointer;
  }
  .catalog-products-head { flex-wrap: wrap; }
  .catalog-export-products { min-width: 0; }
  .catalog-export-count {
    margin-right: auto; color: var(--muted);
    font-size: var(--fs-small); font-weight: 650;
  }
  .catalog-product-groups { display: grid; gap: 8px; }
  .catalog-group { overflow: hidden; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); }
  .catalog-group summary { display: flex; align-items: center; gap: 8px; padding: 8px 10px; cursor: pointer; list-style: none; }
  .catalog-group summary::-webkit-details-marker { display: none; }
  .catalog-group summary small { color: var(--muted); font-size: var(--fs-caption); }
  .catalog-group-items { display: grid; gap: 2px; padding: 4px 0; }
  .catalog-item-row { display: flex; align-items: center; gap: 8px; padding: 4px 2px; font-size: var(--fs-small); font-weight: 550; color: var(--ink-2); }
  .catalog-item-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .catalog-item-price { flex: none; color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
  .catalog-item-thumb {
    flex: none; width: 40px; height: 30px; padding: 0;
    overflow: hidden; border: 1px dashed var(--line-2); border-radius: var(--r-xs);
    background: var(--surface-2); cursor: pointer;
  }
  .catalog-item-thumb.has-photo { border-style: solid; }
  .catalog-item-thumb span { display: block; width: 100%; height: 100%; }
  .catalog-item-thumb .catalog-thumb-empty { display: grid; place-items: center; color: var(--muted); font-size: var(--fs-body); font-weight: 700; }

  .catalog-photo-editor {
    display: grid; gap: 10px; margin: 4px 0 8px 20px; padding: 10px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
  }
  .catalog-photo-stage {
    width: 100%; height: 118px; display: grid; place-items: center;
    border-radius: var(--r-sm); background: var(--amber-soft);
    cursor: grab; touch-action: none;
  }
  .catalog-photo-stage:active { cursor: grabbing; }
  .catalog-photo-stage.is-empty span { padding: 0 14px; color: var(--muted); font-size: var(--fs-caption); text-align: center; }
  .catalog-photo-tools { display: grid; gap: 8px; align-content: start; }
  .catalog-photo-zoom { display: grid; gap: 4px; color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
  .catalog-photo-zoom input[type="range"] { width: 100%; min-height: 0; padding: 0; border: 0; background: transparent; box-shadow: none; accent-color: var(--ink); }
  .catalog-photo-actions { display: flex; flex-wrap: wrap; gap: 6px; }
  .catalog-photo-tools small { color: var(--muted); font-size: var(--fs-caption); line-height: 1.4; }
  .catalog-live-preview { min-height: 0; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-3); overflow: hidden; }
  /* El iframe se dibuja a tamaño real (820px) y app.js lo achica con transform:
     sin este ancho fijo se queda en los 300px por defecto y recorta la hoja. */
  .catalog-live-preview iframe { width: 820px; height: 100%; border: 0; display: block; transform-origin: top left; }

  /* ── Dashboard de empresas: tarjetas auto-fit con scroll interno propio ── */
  .coffee-dashboard-section { padding-bottom: 12px; }
  .coffee-company-dashboard { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); gap: 10px; max-height: 470px; overflow: auto; margin-top: 14px; padding-right: 2px; }
  .coffee-company-card { display: grid; gap: 12px; min-width: 0; padding: 12px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .coffee-company-card--ok { border-color: color-mix(in srgb, var(--green) 24%, transparent); background: linear-gradient(180deg, var(--surface), var(--green-soft)); }
  .coffee-company-card--watch { border-color: color-mix(in srgb, var(--amber) 24%, transparent); background: linear-gradient(180deg, var(--surface), var(--amber-soft)); }
  .coffee-company-card--stale { border-color: color-mix(in srgb, var(--red) 24%, transparent); background: linear-gradient(180deg, var(--surface), var(--red-soft)); }
  .coffee-company-card-head { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 8px; align-items: start; }
  .coffee-company-card h3 { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-body-lg); font-weight: 700; line-height: 1.2; }
  .coffee-company-card p { margin: 4px 0 0; color: var(--muted); font-size: var(--fs-small); }
  .coffee-company-metrics { display: grid; grid-template-columns: 1.15fr .95fr .55fr; gap: 8px; }
  .coffee-company-metrics div { min-width: 0; border-top: 1px solid var(--line); padding-top: 8px; }
  .coffee-company-metrics span { display: block; color: var(--muted); font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; }
  .coffee-company-metrics strong { display: block; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-body); font-weight: 700; }

  /* ── Responsive ──
     900 (v1: 980 ad-hoc): formularios y grillas de dos filas se apilan. */
  @media (max-width: 900px) {
    .coffee-form-grid, .coffee-event-product-add, .coffee-event-product-row,
    .coffee-quote-request, .coffee-quote-params, .coffee-quote-title,
    .coffee-quote-add-fields, .coffee-quote-add-fields--free, .coffee-quote-notes,
    .coffee-catalog-form {
      grid-template-columns: 1fr;
    }
    .calendar-event-grid { grid-template-columns: 1fr; }
    .coffee-quote-total { justify-self: stretch; text-align: left; }
    .coffee-catalog-description-field { grid-column: auto; }
    .catalog-live-grid { grid-template-columns: 1fr; }
    .catalog-live-preview { min-height: 340px; }
  }

  @media (max-width: 680px) {
    .coffee-event-product-modes,
    .coffee-event-product-add[data-coffee-product-panel="catalog"] { grid-template-columns: 1fr; }
    .coffee-event-product-total { display: grid; }
    .coffee-quote-toggle-row { align-items: stretch; }
  }
}

/* líneas: ~217 de presupuesto 150 — excedido y documentado (como toolbar/
   feedback/login-boot en P2): la vista concentra 6 sub-áreas reales (hub,
   ficha+picker de 3 modos, cotizador completo, modal catálogo admin, editor
   de catálogo en vivo, dashboard de empresas), varias con placas propias
   sin dueño en 04-componentes (ver gaps arriba) que no se reusan fuera de
   Eventos — mismo criterio que pedidos.css. */
/* ══ exportación contable — data-view="accounting-export" ═════════════════
   Cubre: .accounting-export-hero/-layout/-builder/-review/-modes/-mode/
     -mode-check/-folders/-hr/-toggle/-password/-no-permission/-submit/
     -checks/-exceptions/-all-clear/-note/-history/-footnote.
   Fuente v1: styles.css ~L16047-16144, reescrita sobre los componentes
     accounting-panel, campo, botón, badge y tabla de la v2.
   Estados: modo activo, hover/focus, controles disabled durante descarga,
     expediente preliminar/definitivo y lista de excepciones vacía.
   ═════════════════════════════════════════════════════════════════════════ */

@layer vistas {
  #view-accounting-export .section-head { align-items: flex-end; margin-bottom: 14px; }

  .accounting-export-hero {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    margin-bottom: 16px;
    padding: 20px;
    border: 1px solid var(--line);
    border-left: 4px solid var(--amber);
    border-radius: var(--r);
    background: linear-gradient(120deg, var(--amber-soft), var(--surface));
  }
  .accounting-export-hero.is-definitive {
    border-left-color: var(--green);
    background: linear-gradient(120deg, var(--green-soft), var(--surface));
  }
  .accounting-export-hero h3 { margin: 8px 0 4px; font-size: var(--fs-title); }
  .accounting-export-hero p { max-width: 650px; margin: 0; color: var(--muted); font-size: var(--fs-small); line-height: 1.5; }
  .accounting-export-hero dl { display: grid; grid-template-columns: repeat(4, minmax(92px, 1fr)); gap: 8px; margin: 0; }
  .accounting-export-hero dl div { min-width: 100px; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-sm); background: color-mix(in srgb, var(--surface) 78%, transparent); }
  .accounting-export-hero dt { color: var(--muted); font-size: var(--fs-caption); font-weight: 800; letter-spacing: .07em; text-transform: uppercase; }
  .accounting-export-hero dd { margin: 4px 0 0; color: var(--ink); font-size: var(--fs-small); font-weight: 700; }

  .accounting-export-layout {
    display: grid;
    grid-template-columns: minmax(0, 1.45fr) minmax(300px, .75fr);
    gap: 20px;
    align-items: start;
  }
  .accounting-export-builder,
  .accounting-export-review { display: grid; gap: 16px; }
  .accounting-export-builder .accounting-panel,
  .accounting-export-review .accounting-panel { margin-bottom: 0; }

  .accounting-export-modes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
  .accounting-export-mode {
    position: relative;
    min-height: 126px;
    padding: 16px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--ink);
    text-align: left;
    cursor: pointer;
    transition: border-color var(--t), box-shadow var(--t), transform var(--t);
  }
  .accounting-export-mode:hover { border-color: var(--accent); transform: translateY(-1px); }
  .accounting-export-mode.active { border-color: var(--ink); box-shadow: inset 0 0 0 1px var(--ink), var(--shadow-sm); }
  .accounting-export-mode strong { display: block; padding-right: 24px; font-size: var(--fs-small); line-height: 1.3; }
  .accounting-export-mode small { display: block; margin-top: 8px; color: var(--muted); font-size: var(--fs-caption); line-height: 1.45; }
  .accounting-export-mode-check {
    position: absolute; top: 14px; right: 14px;
    display: grid; place-items: center; width: 22px; height: 22px;
    border: 1px solid var(--line); border-radius: 50%; color: transparent;
  }
  .accounting-export-mode.active .accounting-export-mode-check { border-color: var(--ink); background: var(--ink); color: var(--surface); }

  .accounting-export-folders { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .accounting-export-folders article { display: flex; gap: 12px; padding: 12px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); }
  .accounting-export-folders b { display: grid; flex: 0 0 30px; height: 30px; place-items: center; border-radius: var(--r-sm); background: var(--ink); color: var(--surface); font-size: var(--fs-caption); }
  .accounting-export-folders :is(strong, span) { display: block; }
  .accounting-export-folders strong { font-size: var(--fs-caption); }
  .accounting-export-folders span { margin-top: 4px; color: var(--muted); font-size: var(--fs-caption); line-height: 1.4; }

  .accounting-export-hr { display: grid; grid-template-columns: 1fr auto; gap: 20px; align-items: center; }
  .accounting-export-hr h3 { margin: 4px 0; font-size: var(--fs-h3); }
  .accounting-export-hr p { max-width: 610px; margin: 0; color: var(--muted); font-size: var(--fs-caption); line-height: 1.45; }
  .accounting-export-toggle { display: flex; align-items: center; gap: 8px; color: var(--ink); font-size: var(--fs-caption); font-weight: 700; cursor: pointer; }
  .accounting-export-toggle input { width: 18px; height: 18px; accent-color: var(--ink); }
  .accounting-export-password { grid-column: 1 / -1; display: grid; gap: 6px; font-size: var(--fs-caption); font-weight: 700; }
  .accounting-export-password small { color: var(--muted); font-weight: 400; }
  .accounting-export-no-permission { max-width: 280px; padding: 10px 12px; border-radius: var(--r-sm); background: var(--surface-2); color: var(--muted); font-size: var(--fs-caption); line-height: 1.4; }

  .accounting-export-submit {
    display: flex; align-items: center; justify-content: space-between;
    gap: 20px; padding: 16px 20px; border-radius: var(--r);
    background: var(--ink); color: var(--surface);
  }
  .accounting-export-submit :is(strong, span) { display: block; }
  .accounting-export-submit strong { font-size: var(--fs-small); }
  .accounting-export-submit span { margin-top: 4px; color: color-mix(in srgb, var(--surface) 68%, transparent); font-size: var(--fs-caption); }
  .accounting-export-submit .primary-action { flex: none; background: var(--surface); color: var(--ink); }

  .accounting-export-checks { display: grid; gap: 2px; }
  .accounting-export-checks div { display: flex; justify-content: space-between; gap: 16px; padding: 8px 0; border-bottom: 1px solid var(--line); }
  .accounting-export-checks div:last-child { border-bottom: 0; }
  .accounting-export-checks span { color: var(--muted); font-size: var(--fs-caption); }
  .accounting-export-checks strong { font-size: var(--fs-caption); font-variant-numeric: tabular-nums; }

  .accounting-export-exceptions { display: grid; gap: 8px; max-height: 300px; margin: 0; padding: 0; overflow: auto; list-style: none; }
  .accounting-export-exceptions li { padding: 10px 12px; border-left: 3px solid var(--amber); border-radius: var(--r-sm); background: var(--amber-soft); }
  .accounting-export-exceptions li.is-crítica { border-left-color: var(--red); background: var(--red-soft); }
  .accounting-export-exceptions li :is(span, strong) { display: block; }
  .accounting-export-exceptions li span { color: var(--muted); font-size: var(--fs-caption); font-weight: 800; letter-spacing: .07em; text-transform: uppercase; }
  .accounting-export-exceptions li strong { margin-top: 4px; color: var(--ink-2); font-size: var(--fs-caption); line-height: 1.4; }
  .accounting-export-all-clear { padding: 12px; border-radius: var(--r-sm); background: var(--green-soft); color: var(--green); font-size: var(--fs-caption); font-weight: 700; }
  .accounting-export-note { padding: 14px 16px; border: 1px dashed var(--line-2); border-radius: var(--r-sm); color: var(--muted); }
  .accounting-export-note strong { color: var(--ink-2); font-size: var(--fs-caption); }
  .accounting-export-note p { margin: 4px 0 0; font-size: var(--fs-caption); line-height: 1.45; }
  .accounting-export-history { margin-top: 20px; }
  .accounting-export-history code { color: var(--muted); font-size: var(--fs-caption); }
  .accounting-export-footnote { margin: 12px 4px 0; color: var(--muted); font-size: var(--fs-caption); line-height: 1.5; }

  @media (max-width: 900px) {
    .accounting-export-hero { align-items: flex-start; flex-direction: column; }
    .accounting-export-hero dl { width: 100%; grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .accounting-export-layout,
    .accounting-export-modes,
    .accounting-export-folders,
    .accounting-export-hr { grid-template-columns: 1fr; }
    .accounting-export-submit { align-items: stretch; flex-direction: column; }
  }
}

/* líneas: ~220 de presupuesto 150 — vista completa con cinco sub-sistemas */
/* ══ flujo-de-caja — data-view="cashflow" ════════════════════════════════════
   Cubre: #view-cashflow .flow-head (solo el margen: el tamaño de sus
     .icon-button queda para 05-vistas/estado-resultados.css, ver NO cubre),
     .cashflow-evidence-banner(.has-data/.needs-data + div:first-child),
     .cashflow-balance-pending, .cashflow-layout, .cashflow-trend-panel,
     .cashflow-table-wrap, .cashflow-table (alineación + 1ª columna +
     tr.is-selected — el resto de la receta lo da la tabla nativa de
     02-base: th/td/tabular-nums/hover/vacío ya calzan sin retoque).
   Fuente v1: styles.css ~L9372 (.flow-head, solo margin-bottom — el resto de
     esa regla es de "flow", ver Nota), ~L9857-9889 (banner + kpi-grid margin
     + trend-panel + tabla, fusionadas), ~L10184-10201 (responsive 1050/720
     → consolidado a 1100/680), ~L15993 (.cashflow-balance-pending span,
     capa aparte, mismo selector).
   Nota de propiedad: `.cashflow-evidence-banner`, `.cashflow-balance-
     pending`, `.cashflow-layout`, `.cashflow-trend-panel`, `.cashflow-
     table(-wrap)` no aparecen en ninguna otra vista (verificado con grep
     en app.js/styles.css) ni en 04-componentes — esta vista es también su
     única definición de identidad visual además de su geometría, igual
     que 05-vistas/cartolas-bancos.css y pedidos.css. La tabla es la
     excepción parcial: solo alineación/1ª columna/estado seleccionado son
     de acá, el resto ya es la receta canon de `table`/`th`/`td`.
   Casos límite: sin movimientos confirmados → fila `.empty` colspan=4
     (ya vestida en 02-base, sin regla aquí); "Por conciliar"/saldos sin
     cartola aún → texto "—"/"Pendiente de cartolas" plano, sin marca
     visual propia; variación neta y neto mensual negativos → `.is-negative`
     (utilidades.css) puesto directo en el `<strong>`/`<td>`, no en esta capa.
   NO cubre — decidido en otra parte, no se resuelve aquí (CONVENCIONES §8):
     `.flow-head .icon-button` (tamaño 20px/36×36) — NOTAS-P2 lo asigna a
     "Estado de resultados" (`flow`), aunque el mismo `.header-period-nav`
     que lo contiene se reubica al `#phPeriod` global antes de pintarse
     (ambas vistas quedan con `.book-tools` vacío o casi); `.finance-
     summary-list` (panel "Controles") — familia compartida con Resumen
     financiero, ya documentada sin dueño en resumen-financiero.css;
     display/columnas/tarjeta de `.cashflow-kpi-grid` — misma familia que
     `.finance-reading-grid`/`.finance-support-grid`, candidata al `:is()`
     de kpi.css (hoy en su presupuesto exacto 300/300); aquí solo se porta
     su margen propio. `.finance-status-badge` ya vive completo en badge.css.
   Cambios visibles: padding del banner 17/18→16/20 (escala de espaciado);
     gap interno 5→6px (misma razón); tamaños de letra fuera de escala
     (10px/9px de subcaption y saldo pendiente) suben al piso de la escala,
     `--fs-caption` (11px); peso 620/430 de la 1ª columna de tabla redondea
     a 650/400 (escala cerrada de pesos); `.cashflow-balance-pending strong`
     no se porta: coincide con el `--ink` heredado por defecto, era
     redundante.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Cabecera de vista: solo el respiro antes del contenido — el resto
     (flex/gap/align) ya lo da .section-head en panel.css. Scopeada porque
     .flow-head también vive en #view-flow (misma clase, otra vista). */
  #view-cashflow .flow-head { margin-bottom: 14px; }

  /* ── Banda de estado de conciliación ──────────────────────────────────
     Icono de estado (badge) + lectura a la izquierda, saldo de cuentas
     conciliadas a la derecha; el tinte cambia entero según haya o no
     movimiento verificado. */
  .cashflow-evidence-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    padding: 16px 20px;
    margin-bottom: 16px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface-2);
  }

  .cashflow-evidence-banner.has-data {
    border-color: color-mix(in srgb, var(--green) 24%, transparent);
    background: var(--green-soft);
  }

  .cashflow-evidence-banner.needs-data {
    border-color: color-mix(in srgb, var(--amber) 24%, transparent);
    background: var(--amber-soft);
  }

  /* Badge (col 1, fila 1) · cifra/título (col 2, fila 1) · nota (col 2,
     fila 2, por eso la ubicación explícita). */
  .cashflow-evidence-banner > div:first-child {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 6px 10px;
  }

  .cashflow-evidence-banner > div:first-child small {
    grid-column: 2;
    color: var(--muted);
    line-height: 1.4;
  }

  .cashflow-balance-pending {
    display: grid;
    flex: none;
    text-align: right;
  }

  .cashflow-balance-pending small { color: var(--muted); }

  .cashflow-balance-pending span {
    display: block;
    margin-top: 2px;
    color: var(--muted);
    font-size: var(--fs-caption);
  }

  /* Solo el respiro bajo la tira: la tarjeta/columnas quedan pendientes
     de subir a kpi.css (ver NO cubre). */
  .cashflow-kpi-grid { margin-bottom: 16px; }

  /* ── Dos columnas parejas: fuente de movimientos + controles ─────────── */
  .cashflow-layout {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
  }

  .cashflow-trend-panel { margin-top: 16px; }

  /* ── Tabla de movimientos/tendencia: la receta canon de table/th/td de
     02-base ya calza (mismo tamaño, mismo tono de header); solo falta
     alinear las columnas de cifra y distinguir la 1ª columna de rótulo. */
  .cashflow-table-wrap { overflow-x: auto; }
  .cashflow-table { min-width: 500px; }   /* dispara el scroll antes de apretar columnas */

  .cashflow-table th,
  .cashflow-table td { text-align: right; }
  .cashflow-table th:first-child,
  .cashflow-table td:first-child { text-align: left; }

  .cashflow-table td:first-child { color: var(--ink-2); font-weight: 650; }
  .cashflow-table td:first-child small {
    display: block;
    margin-top: 2px;
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 400;
  }

  /* Fila del mes activo en la tendencia anual. */
  .cashflow-table tr.is-selected td { background: var(--blue-soft); }

  @media (max-width: 1100px) {
    .cashflow-layout { grid-template-columns: 1fr; }
  }

  @media (max-width: 680px) {
    .cashflow-evidence-banner { flex-direction: column; align-items: flex-start; }
    .cashflow-evidence-banner > div:first-child { grid-template-columns: 1fr; }
    .cashflow-evidence-banner > div:first-child small { grid-column: 1; }
    .cashflow-balance-pending { text-align: left; }
  }
}

/* líneas: ~159 de presupuesto 150 (+9: la vista es también identidad visual
   del banner de conciliación y la tabla — ver Nota de propiedad) */
/* ══ importar — #view-import (data-view="import") ═══════════════════════════
   Jerarquía de dos niveles inspirada en Bancos y conciliación:
   1. un drawer general de fuentes;
   2. cuatro segmentos siempre visibles dentro de él;
   3. los métodos de cada segmento como drawers independientes.

   Los formularios e IDs originales se conservan. Sólo cambia su composición:
   la vista deja de mostrar toda la información simultáneamente y revela cada
   proceso en contexto, con apertura/cierre suave y sin perder claridad.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  #view-import { max-width: none; }

  /* ── Nivel 1: contenedor general de fuentes ───────────────────────── */
  .import-source-drawer {
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: var(--shadow-xs);
    transition: border-color var(--t), box-shadow var(--t);
  }

  .import-source-drawer[open] {
    border-color: var(--line-2);
    box-shadow: var(--shadow-sm);
  }

  .import-source-drawer > summary {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 18px;
    min-height: 78px;
    padding: 14px 54px 14px 18px;
    list-style: none;
    cursor: pointer;
    transition: background var(--t);
  }
  .import-source-drawer > summary::-webkit-details-marker { display: none; }
  .import-source-drawer > summary:hover { background: var(--surface-2); }

  .import-source-drawer > summary > div {
    display: grid;
    gap: 3px;
    min-width: 0;
  }
  .import-source-drawer > summary small {
    color: var(--muted-2);
    font-size: var(--fs-caption);
    font-weight: 800;
    letter-spacing: .075em;
    text-transform: uppercase;
  }
  .import-source-drawer > summary strong {
    color: var(--ink);
    font-size: var(--fs-h3);
    line-height: 1.2;
  }
  .import-source-drawer > summary > div > span {
    overflow: hidden;
    color: var(--muted);
    font-size: var(--fs-small);
    line-height: 1.35;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .import-source-drawer-action {
    flex: 0 0 auto;
    padding: 7px 10px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--ink-2);
    font-size: var(--fs-caption);
    font-weight: 700;
    white-space: nowrap;
  }
  .import-source-drawer > summary::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 21px;
    width: 8px;
    height: 8px;
    border-right: 1.5px solid var(--muted);
    border-bottom: 1.5px solid var(--muted);
    transform: translateY(-65%) rotate(45deg);
    transition: transform var(--t-lg), border-color var(--t);
  }
  .import-source-drawer[open] > summary::after {
    border-color: var(--accent);
    transform: translateY(-30%) rotate(225deg);
  }

  .import-source-drawer-body {
    padding: 16px 18px 18px;
    border-top: 1px solid var(--line);
    background: color-mix(in srgb, var(--surface-2) 42%, var(--surface));
  }

  /* ── Nivel intermedio: cuatro áreas simultáneas ───────────────────── */
  .import-layout {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 18px;
    align-items: start;
  }

  .import-column {
    --import-accent: var(--green);
    display: grid;
    gap: 8px;
    min-width: 0;
    align-content: start;
  }

  .import-column--purchases { --import-accent: var(--green); }
  .import-column--sales     { --import-accent: var(--blue); }
  .import-column--recipes   { --import-accent: var(--teal); }
  .import-column--items     { --import-accent: var(--amber); }

  .import-column-head {
    display: flex;
    align-items: end;
    justify-content: space-between;
    gap: 12px;
    min-height: 59px;
    padding: 0 3px 7px;
  }
  .import-column-head > div { min-width: 0; }
  .import-column-head :is(.eyebrow, h2, div > span) { display: block; }
  .import-column-head .eyebrow {
    margin: 0 0 2px;
    color: var(--import-accent);
  }
  .import-column-head h2 {
    margin: 0;
    color: var(--ink);
    font-size: var(--fs-h3);
    line-height: 1.2;
  }
  .import-column-head div > span {
    margin-top: 3px;
    overflow: hidden;
    color: var(--muted);
    font-size: var(--fs-small);
    line-height: 1.35;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .import-column-count {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    color: var(--ink-2);
    font-size: var(--fs-caption);
    font-weight: 700;
    white-space: nowrap;
  }
  .import-column-count::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--import-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--import-accent) 10%, transparent);
  }

  /* ── Nivel 2: métodos apilados dentro de cada área ───────────────── */
  .import-column-body {
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
    box-shadow: var(--shadow-xs);
  }

  .import-column-body > .import-method {
    display: block;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: var(--surface);
  }
  .import-column-body > .import-method + .import-method {
    border-top: 1px solid var(--line);
  }

  .import-method-head {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 12px;
    min-height: 66px;
    padding: 10px 44px 10px 46px;
    list-style: none;
    cursor: pointer;
    transition: background var(--t), box-shadow var(--t);
  }
  .import-method-head::-webkit-details-marker { display: none; }
  .import-method-head:hover {
    background: color-mix(in srgb, var(--import-accent) 5%, var(--surface));
  }
  .import-method[open] > .import-method-head {
    background: color-mix(in srgb, var(--import-accent) 7%, var(--surface));
    box-shadow: inset 0 -1px var(--line);
  }
  .import-method-head > div:first-child { min-width: 0; }
  .import-method-head::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 15px;
    width: 19px;
    height: 19px;
    border: 1.5px solid color-mix(in srgb, var(--import-accent) 70%, var(--line));
    border-radius: 50%;
    background: color-mix(in srgb, var(--import-accent) 7%, var(--surface));
    transform: translateY(-50%);
    transition: background var(--t), border-color var(--t), box-shadow var(--t);
  }
  .import-method[open] > .import-method-head::before {
    border-color: var(--import-accent);
    background: var(--import-accent);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--import-accent) 11%, transparent);
  }
  .import-method-head::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 17px;
    width: 7px;
    height: 7px;
    border-right: 1.5px solid var(--muted);
    border-bottom: 1.5px solid var(--muted);
    transform: translateY(-65%) rotate(45deg);
    transition: transform var(--t-lg), border-color var(--t);
  }
  .import-method[open] > .import-method-head::after {
    border-color: var(--import-accent);
    transform: translateY(-25%) rotate(225deg);
  }

  .import-method-head :is(.eyebrow, h3, .muted) { display: block; }
  .import-method-head .eyebrow {
    margin: 0 0 2px;
    color: var(--muted-2);
  }
  .import-method-head h3 {
    margin: 0;
    color: var(--ink);
    font-size: var(--fs-body-lg);
    line-height: 1.25;
  }
  .import-method-head .muted {
    margin: 3px 0 0;
    overflow: hidden;
    color: var(--muted);
    font-size: var(--fs-caption);
    line-height: 1.35;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .import-method-tag {
    padding: 4px 7px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--import-accent) 8%, var(--surface));
    color: var(--import-accent);
    font-size: var(--fs-caption);
    font-weight: 700;
    white-space: nowrap;
    transition: background var(--t), color var(--t);
  }
  .import-method[open] .import-method-tag {
    background: color-mix(in srgb, var(--import-accent) 14%, var(--surface));
  }

  /* El cuerpo del método gana una sangría que conserva la jerarquía. */
  .import-method > form,
  .import-method > .manual-upload-grid {
    padding: 14px 14px 16px 46px;
    background: color-mix(in srgb, var(--surface-2) 54%, var(--surface));
  }

  .import-column .form-grid {
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  }
  .import-column .catalog-actions {
    display: grid;
    grid-template-columns: 1fr;
    gap: 12px;
    align-items: end;
  }
  .import-column .ocr-hints {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
  }

  /* XML y OCR siguen siendo dos rutas, ahora dentro del método Manual. */
  .import-column .manual-upload-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 14px;
  }
  .import-column .ocr-zone {
    padding-top: 14px;
    border-top: 1px solid var(--line);
  }

  .import-column .button-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    align-items: stretch;
  }
  .import-column :is(.secondary-action, .primary-action) { width: 100%; }
  .import-column .inline-status {
    grid-column: 1 / -1;
    justify-self: start;
    max-width: 100%;
    overflow-wrap: anywhere;
  }
  .import-column .import-progress { grid-column: 1 / -1; }
  .import-column .debug-box { max-height: 210px; overflow: auto; }

  /* ── Apertura/cierre suave de ambos niveles ──────────────────────── */
  @supports selector(details::details-content) {
    #view-import { interpolate-size: allow-keywords; }

    :is(.import-source-drawer, .import-method)::details-content {
      block-size: 0;
      overflow-y: clip;
      opacity: 0;
      transform: translateY(-5px);
      transition:
        block-size var(--t-lg),
        content-visibility .26s allow-discrete,
        opacity var(--t),
        transform var(--t-lg);
    }
    :is(.import-source-drawer, .import-method)[open]::details-content {
      block-size: auto;
      opacity: 1;
      transform: translateY(0);
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .import-method[open] > :is(form, .manual-upload-grid) {
      animation: import-method-body-in .22s var(--ease) backwards;
    }
  }

  @keyframes import-method-body-in {
    from { opacity: 0; transform: translateY(-4px); }
  }

  /* ── Responsive ───────────────────────────────────────────────────── */
  @media (max-width: 900px) {
    .import-layout { grid-template-columns: 1fr; }
  }

  @media (max-width: 680px) {
    .import-source-drawer > summary {
      min-height: 72px;
      padding: 12px 42px 12px 14px;
    }
    .import-source-drawer-action { display: none; }
    .import-source-drawer > summary::after { right: 17px; }
    .import-source-drawer-body { padding: 12px; }
    .import-layout { gap: 15px; }
    .import-column-head { align-items: start; }
    .import-column-head div > span { white-space: normal; }
    .import-column-count { padding-right: 0; }
    .import-method-head {
      grid-template-columns: minmax(0, 1fr);
      gap: 5px;
      padding: 10px 38px 10px 42px;
    }
    .import-method-tag { justify-self: start; }
    .import-method-head .muted { white-space: normal; }
    .import-method > form,
    .import-method > .manual-upload-grid { padding: 12px; }
    .import-column .form-grid,
    .import-column .ocr-hints,
    .import-column .button-row { grid-template-columns: 1fr; }
  }
}
/* ══ insumos-clasificar (data-view="review") ═══════════════════════════════
   Cubre: .review-list, .review-bulk-bar, .review-select-all, .review-row,
          .review-check-cell, .review-title, .review-meta
   Fuente v1: styles.css ~L6165-6220 (REVIEW ROWS), ~L6327-6334
          (.review-title/.review-meta), ~L10388 (responsive 980 → 900).
   Casos límite: descripción larga → envuelve (sin ellipsis: es el dato
          principal de la fila); proveedor/folio/fecha ausentes llegan como
          "-" ya armado por JS; fila vacía = .empty (componente, sin regla
          aquí); selección múltiple = .is-selected.
   Deuda NO portada: v1 solo colapsaba a 1 columna bajo los 980px de
          viewport y dejaba la fila de 9 columnas fluida arriba de eso — con
          sidebar 238px + padding del workspace, el título (fr flexible) se
          aplastaba a una palabra por línea en todo laptop de 1280-1440px
          reales (verificado en vivo, no es un caso límite raro). En vez de
          seguir estrujando columnas, .review-row fija un min-width y
          .review-list scrollea horizontal solo ella (mismo patrón acotado
          que .table-wrap en tabla.css — la cabecera y la barra masiva no
          se arrastran con la tabla).
   Nota: .table-section/.section-head/.search-box/.mini-input/.row-check/
          .empty/.inline-status/.settings-area-tabs ya vienen de
          04-componentes — esta vista NO los toca, solo acomoda .review-row
          como fila de "tabla" (cada fila es un <form> propio, por eso no es
          una <table> real y esta vista es dueña de su geometría de columnas).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Barra de selección masiva: cuelga bajo la cabecera de sección ────── */
  .review-bulk-bar {
    margin-top: 12px;
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface-2);
  }
  .review-bulk-bar .secondary-action { margin-left: auto; }   /* botón al ras derecho */

  .review-select-all {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: var(--fs-body);
    font-weight: 700;   /* v1: 750 → escala */
    color: var(--ink);
  }

  /* ── Lista de líneas pendientes: scroll horizontal acotado (no se lleva
     la cabecera ni la barra masiva de arriba, ver "Deuda NO portada"). ──── */
  .review-list {
    margin-top: 12px;
    display: grid;
    gap: 8px;
    overflow-x: auto;
  }

  /* Fila = <form>: 9 columnas fijas para que los 4 mini-input de mapeo
     (nombre/UdM/familia/subfamilia) queden alineados entre filas. */
  .review-row {
    display: grid;
    grid-template-columns: 24px minmax(0, 1.2fr) 100px 110px minmax(180px, .8fr) 90px 130px 140px auto;
    /* Sin min-width (como v1): la fila entera —incluido "Aprobar"— tiene que
       caber en pantalla. Con el mínimo de 1180px la tabla scrolleaba y el
       botón de la última columna quedaba cortado, que es justo la acción de
       la vista. El minmax(0,1.2fr) del nombre absorbe la compresión. */
    gap: 10px;
    align-items: center;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
    padding: 12px;   /* v1: 11px → escala más cercana */
    transition: border-color var(--t);
  }
  .review-row:hover { border-color: var(--line-2); }

  /* Fila marcada para aprobación masiva: tinte de acento vía fórmula de
     token (v1 traía rgba(0,107,173,…) crudo → var(--accent) + color-mix). */
  .review-row.is-selected {
    border-color: color-mix(in srgb, var(--accent) 36%, transparent);
    background: color-mix(in srgb, var(--accent) 5%, var(--surface));
  }

  .review-check-cell {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  .review-title { font-size: var(--fs-body); font-weight: 700; }
  .review-meta {
    margin-top: 2px;
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 550;   /* v1: 530 → escala */
  }

  /* ── Responsive: bajo 900 la fila-form apila en una columna y el
     min-width deja de regir (ver "Deuda NO portada" arriba). Entre 900 y
     ~1180 de contenido real, .review-list scrollea; no hay agujero sin
     tratar. (v1: 980 → 900, el más cercano de los 3 permitidos). */
  @media (max-width: 900px) {
    .review-row { grid-template-columns: 1fr; min-width: 0; }
  }
}

/* líneas: ~107 de presupuesto 150 (cabecera larga por la deuda de v1
   documentada — el min-width + scroll acotado la reemplaza en pocas
   líneas de regla real) */
/* ══ inventario — data-view="inventory" ═════════════════════════════════════
   Cubre: #inventoryContent (esqueleto), .inventory-head, promoción de KPIs al
     page-header (.inv-summary-strip oculto en mensual, .inv-year-kpi-strip
     visible en anual), .inv-settings-panel(-head/-actions), .inv-subfamily-
     picker/-check, .inv-table-panel, .inv-subfamily-summary(--annual),
     .inv-summary-title, .inv-purchase-plan-panel, .inv-plan-board(__item),
     .inv-purchase-plan-table (resuelve NOTAS-P2 #6), .inv-purchase-trend-panel,
     .inv-trend-controls/-actions/-picker/-check, .inv-purchase-trend-chart,
     .inventory-import-grid/-help, .inventory-import-preview-wrap.
   NO se tocan aquí (ya resueltos arriba en la cascada): .book-tools/.segmented-
     control/.header-period-nav (toolbar.css/page-header.css), .section-head/
     .eyebrow (panel.css/base.css), .modal-backdrop/.modal-panel/.modal-grid/
     .modal-actions/.inventory-import-modal (modal.css), .upload-zone/
     .inventory-import-upload (dropzone.css), .table-wrap (tabla.css), la
     grilla/tarjeta de .inv-summary-strip en sí (kpi.css), .inv-unit-pill/
     .inv-src-badge/.inv-temp-badge (badge.css), alto y densidad de fila de
     .inv-tabulator/.inv-summary-tabulator(--annual)/.inv-tabulator-total-row/
     .inv-summary-detail-row (07-terceros.css).
   Fuente v1: styles.css ~L10988-11180 (cabecera, settings, import),
     ~L11440-11530 (summary-strip base + subfamily-summary — superseded por
     v120/kpi.css salvo lo documentado abajo), ~L11564-11630 (purchase-plan-
     table + board), ~L11800-11970 (trend controls/picker/chart, alturas
     tabulator), ~L13605-13670 (v120 auto-fit + ocultamiento del strip
     mensual, promovido al page-header — ver renderPageHeader('inventory') y
     PAGE_HEADER_STATS_SUPPRESSED_VIEWS en app.js) — fusionadas last-wins.
     .inv-tabulator--year/#inventoryYearTable (styles.css ~L11919): MUERTA,
     sin emisor en app.js (la tabla anual densa es #inventoryAnnualSummaryTable
     con clase .inv-summary-tabulator--annual) — no se porta.
   Casos límite: nombre de subfamilia/proveedor largo → ellipsis donde v1 lo
     hacía; nota anual larga ("venta gastro neta $X · sin arriendo…") →
     envuelve a la derecha del título sin romper la fila a dos líneas; sin
     ítems/sin datos → JS monta `.empty`/`.inv-error` (feedback.css), nada que
     posicionar aquí; import sin archivo → preview vacía con su placeholder
     HTML estático, altura ya acotada.
   Nota de propiedad: .inv-settings-panel, .inv-table-panel, .inv-subfamily-
     summary, .inv-summary-title, .inv-plan-board(__item), .inv-trend-controls,
     .inv-subfamily-check, .inv-trend-check no son instancia de ningún
     componente de 04-componentes (verificado contra los "Cubre" de panel/
     badge/kpi/campo) — widgets propios de esta vista: fijan también su
     identidad mínima (borde, superficie, tipografía con tokens), no solo su
     acomodo — mismo criterio que 05-vistas/cierre-de-turno.css.
   Excluidas a propósito (dueño ya identificado, no se re-skinea aquí):
     .inv-item-name/-meta, .inv-group-title/-count, .inv-cat-total,
     .inv-editable-cell, .inv-neg, .inv-money-pct, .inv-delete-btn,
     .inv-plan-action/-reason/-decisions/-group-header, .mini-action — todo
     vive dentro de celdas formateadas por Tabulator.js (#inventory*Table):
     tabla.css ya documentó que el prefijo inv- ahí adentro es "Tabulator
     real", skin de 07-terceros.css, no de esta capa.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Esqueleto: pila de secciones con el mismo aire entre todas ── */
  #inventoryContent { display: grid; gap: 14px; }
  #view-inventory .inventory-head { margin-bottom: 12px; }

  /* ── KPIs promovidos al page-header (renderPageHeader('inventory')): el
     strip mensual queda redundante contra las cifras del header y se oculta;
     el strip anual (6 tarjetas con más detalle que las 4 del header) sigue
     mostrándose dentro de la vista — así lo decide v1 (styles.css ~L13666). */
  #view-inventory .inv-summary-strip { display: none; }
  #view-inventory .inv-summary-strip.inv-year-kpi-strip { display: grid; }

  /* ── Superficie compartida: panel blanco, sin sombra permanente (mismo
     canon que .table-section/.overview-panel de panel.css) — 3 secciones
     distintas del inventario reutilizan esta misma receta. ── */
  :is(.inv-settings-panel, .inv-table-panel, .inv-subfamily-summary,
      .inv-purchase-plan-panel, .inv-purchase-trend-panel) {
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface);
  }
  .inv-settings-panel { padding: 12px 14px; }

  /* ── Cabecera de barra: título + nota, reutilizada 6 veces (resumen
     mensual/anual, insumos del mes, carrito, tendencia). ── */
  :is(.inv-summary-title, .inv-trend-controls) {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--line);
  }
  :is(.inv-summary-title, .inv-trend-controls) strong { font-size: var(--fs-body); font-weight: 800; }
  :is(.inv-summary-title, .inv-trend-controls, .inv-settings-head) span {
    color: var(--muted); font-size: var(--fs-small); font-weight: 550;
  }
  .inv-trend-controls { align-items: flex-start; background: var(--surface-2); }
  /* Nota anual larga: envuelve a la derecha del título en vez de forzar dos filas. */
  .inv-subfamily-summary--annual .inv-summary-title { align-items: flex-start; }
  .inv-subfamily-summary--annual .inv-summary-title span { max-width: min(720px, 62vw); white-space: normal; text-align: right; }

  /* ── Panel de ajustes: cabecera + picker de subfamilias en grilla ── */
  .inv-settings-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 12px; }
  .inv-settings-actions { display: flex; flex: none; gap: 8px; }
  .inv-subfamily-picker {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px;
    max-height: 142px; overflow: auto; margin-top: 12px; padding-right: 2px;
  }
  .inv-subfamily-check {
    display: flex; align-items: center; min-width: 0; gap: 8px; padding: 6px 8px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2);
    cursor: pointer;
  }
  .inv-subfamily-check input { flex: none; width: 14px; height: 14px; }
  .inv-subfamily-check span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* ── Carrito de compra sugerido: tarjetas de conteo + tabla agrupada por
     proveedor. Resuelve NOTAS-P2 #6: sub-skin propio con alto fijo (más bajo
     que .inv-tabulator porque conviven con el board arriba), no el genérico
     .inv-summary-tabulator sin altura. ── */
  .inv-plan-board {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 8px;
    padding: 12px 14px; border-bottom: 1px solid var(--line); background: var(--surface-2);
  }
  .inv-plan-board__item {
    min-width: 0; padding: 10px 12px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
  }
  .inv-plan-board__item strong { display: block; font-size: var(--fs-h3); font-weight: 800; }
  .inv-plan-board__item span {
    display: block; margin-top: 4px; overflow: hidden; color: var(--muted);
    font-size: var(--fs-caption); font-weight: 650; text-overflow: ellipsis; white-space: nowrap;
  }
  .inv-plan-board__item--now { box-shadow: inset 4px 0 0 var(--rose), var(--shadow-xs); }
  .inv-plan-board__item--week { box-shadow: inset 4px 0 0 var(--amber), var(--shadow-xs); }
  .inv-plan-board__item--pending { box-shadow: inset 4px 0 0 var(--blue), var(--shadow-xs); }
  .inv-plan-board__item--supplier { box-shadow: inset 4px 0 0 var(--green), var(--shadow-xs); }
  .inv-plan-board__item--cart { box-shadow: inset 4px 0 0 var(--ink), var(--shadow-xs); }
  .inv-plan-board__item--inactive { box-shadow: inset 4px 0 0 var(--muted), var(--shadow-xs); }
  .inv-purchase-plan-table { height: 520px; min-height: 420px; }

  /* ── Tendencia de compra: picker de subfamilias + gráfico ── */
  .inv-trend-actions { display: flex; flex: none; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
  .inv-trend-picker {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(178px, 1fr)); gap: 8px;
    max-height: 118px; overflow: auto; padding: 12px 14px; border-bottom: 1px solid var(--line);
  }
  .inv-trend-check {
    display: flex; align-items: flex-start; min-width: 0; gap: 8px; padding: 6px 8px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2);
    cursor: pointer;
  }
  .inv-trend-check.is-selected { border-color: var(--green); background: var(--green-soft); }
  .inv-trend-check input { flex: none; width: 14px; height: 14px; }
  .inv-trend-check span { min-width: 0; }
  .inv-trend-check strong { display: block; overflow: hidden; font-size: var(--fs-small); text-overflow: ellipsis; white-space: nowrap; }
  .inv-trend-check small { display: block; margin-top: 4px; }
  .inv-purchase-trend-chart { height: 340px; min-height: 300px; }

  /* ── Modal de importación: subida + ayuda en dos columnas ── */
  .inventory-import-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(220px, .9fr); gap: 12px; margin-top: 14px; }
  .inventory-import-help {
    display: grid; align-content: center; gap: 6px; padding: 14px;
    border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2);
  }
  .inventory-import-preview-wrap { max-height: 260px; }

  /* ── Responsive ── */
  @media (max-width: 680px) {
    .inventory-import-grid { grid-template-columns: 1fr; }
  }
}

/* líneas: ~160 de presupuesto 150 — excedido a propósito: 7 widgets sin
   dueño en 04-componentes (panel + cabecera de barra repetida 6x, picker de
   subfamilias, tarjetas del carrito, picker de tendencia) fijan también su
   identidad mínima, mismo criterio que 05-vistas/cierre-de-turno.css. */
/* ══ libro-de-compras — data-view="book" ═══════════════════════════════════
   Cubre: #view-book .book-head (alineación de la cabecera con toolbar
          pesado), .book-wrap (altura de scroll vertical), .book-table
          (ancho total + columnas .supplier-cell/.item-cell/.numeric).
          .book-section, .book-tools, .book-summary, .search-box,
          .date-range-filter, .segmented-control, .table-wrap, .table-action
          NO se tocan aquí: ya son la receta genérica de panel.css/
          toolbar.css/campo.css/kpi.css/tabla.css/boton.css — esta vista solo
          decide cómo se acomoda SU cabecera y SU tabla de 22 columnas.
   Fuente v1: styles.css ~L3606-3612 (.book-head align-items, "TOOLBAR
          GROUPS"), ~L5146-5178 (.book-wrap/.book-table/.supplier-cell),
          ~L5214 (.item-cell), ~L10397-10460 (fusión @980px: cabecera pasa
          a apilada, herramientas a ancho completo) — fusionadas last-wins.
          El resto de @980/@620 que también nombra .book-tools ya lo
          resuelve toolbar.css de forma genérica (mismo resultado visual).
   Casos límite: 22 columnas → el scroll horizontal vive en .table-wrap
          (ya genérico); aquí solo se fuerza el ancho total mínimo para que
          ese scroll aparezca en vez de comprimir columnas ilegibles. Tabla
          vacía / error: colspan="22" ya viene del propio HTML de app.js,
          nada que posicionar. Sin panel lateral de detalle: la función
          renderPurchaseSupplierSummary()/#bookSupplierSummaryList de
          app.js no tiene anclaje en index.php (verificado con grep) — es
          JS huérfano, no se construye geometría para un panel que no existe
          en el DOM actual.
   Decisiones de porte: el peso de .item-cell/.supplier-cell y .doc-type se
          conserva en sus recetas compartidas; el foco programático de una
          compra sí es estado propio de esta vista y se pinta aquí.
   ═══════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Cabecera: título + 3 segmentados + buscador + período + 2 botones ──
     El bloque de herramientas envuelve a 2-3 líneas incluso en --view-max;
     con align-items:center (receta base de .section-head) el título queda
     centrado contra ese bloque alto. flex-start lo ancla arriba. */
  #view-book .book-head { align-items: flex-start; }

  /* ── Techo de scroll de la tabla ──────────────────────────────────────
     Lo que se resta es lo que hay ENCIMA de la tabla más el aire de abajo:
     padding del workspace + barra de herramientas + cabecera de sección +
     16 de padding de la tarjeta + 28 de respiro. Medido, no estimado — si
     cambia la cabecera hay que volver a medir (el valor viejo venía del
     header antiguo con eyebrow y tarjetas KPI, y reservaba 120-140px que
     ya no existen: esa franja quedaba en blanco bajo la tabla). */
  #view-book .book-wrap { max-height: calc(100dvh - var(--nav-alto) - 205px); }

  /* ── Ancho de columnas: 22 campos, la mayoría cifras angostas más dos
     columnas de texto libre (proveedor, insumo) que necesitan piso propio
     para no comprimirse contra las numéricas. */
  #view-book .book-table { min-width: 2440px; }
  #view-book .book-table .supplier-cell { min-width: 200px; }
  #view-book .book-table .item-cell { min-width: 260px; }
  #view-book .book-table th.numeric,
  #view-book .book-table td.numeric { min-width: 96px; }

  /* Fila a la que salta el resumen por proveedor: debe sobrevivir al
     hover genérico de tabla y seguir siendo reconocible al hacer scroll. */
  #view-book .book-row-focus > td { background: var(--blue-soft); }
  #view-book .book-row-focus {
    outline: 2px solid color-mix(in srgb, var(--blue) 28%, transparent);
    outline-offset: -2px;
  }

  /* ── Responsive ──
     900: título y herramientas se apilan (v1 @980) — el bloque de filtros
     ya no compite por ancho contra el título, va a fila completa. */
  @media (max-width: 900px) {
    #view-book .book-tools { width: 100%; }
    #view-book .book-tools > * { width: 100%; }
    #view-book .book-wrap { max-height: calc(100dvh - var(--nav-alto) - 334px); }
  }

  /* 680: cada control de la toolbar ya es su propia fila (toolbar.css) —
     la pila crece más y el scroll de la tabla se recorta un poco más. */
  @media (max-width: 680px) {
    #view-book .book-wrap { max-height: calc(100dvh - var(--nav-alto) - 354px); }
  }
}

/* líneas: ~64 de presupuesto 150 */
/* ══ libro-mayor — data-view="accounting" ═══════════════════════════════════
   Cubre: #view-accounting .section-head (alineación contra el selector de
     periodo), .accounting-ledger-head (grid cuenta + 4 cifras del Libro
     mayor), .accounting-table/.accounting-ledger-table/.trial-balance-table
     (ancho mínimo + fórmula de alineación numérica — una por tabla, difieren
     en columnas), .account-link--row (ancho de la celda Cuenta del
     balance), .financial-entry-lines (columnas Debe/Haber dentro del
     acordeón de cada asiento), .financial-journal-form/-meta/-lines/-line/
     -actions (grid del formulario de asiento manual), .chart-account-grid
     (grilla del Plan de cuentas).
   Fuente v1: styles.css ~L15836+9372 (section-head, fusión align-items +
     margin-bottom), ~L15908-15942 (ledger-head/accounting-table/trial,
     alineación corregida — ver Nota), ~L15944-15958 (account-link--row),
     ~L10134-10179 (financial-entry-lines/financial-journal-*),
     ~L15967-15975 (chart-account-grid), ~L16121-16135 (responsive
     900/680) — fusionadas last-wins.
   Casos límite: cuenta o periodo sin movimientos → fila única ".empty" (ya
     vestida en 02-base); asiento con más de 80 líneas → aviso de una línea
     sin grid propio; glosa/memo/detalle largo → envuelve, no trunca (v1
     tampoco truncaba estas tablas); cifra ausente → "—" de texto plano (JS).
   Nota — alineación del balance corregida: v1 aplicaba una sola fórmula
     (nth-last-child(-n+4)) a las dos tablas de este mayor por igual; en el
     balance (7 columnas: Cuenta + 6 de cifra) dejaba las 2 columnas de
     saldo inicial sin alinear, y habría descuadrado también la fila de
     sub-encabezado (Debe/Haber sueltos, sin la celda Cuenta que sí cuenta
     el body). Se separa en dos reglas por tabla — deuda de v1, no intención
     a preservar (CONVENCIONES §"Cómo portar desde v1"). El gap 13px de
     .financial-journal-form (fuera de escala) baja a 12px, cambio invisible.
   NO cubre — gap de componente, no de esta vista (afecta por igual a
     Cuentas por pagar/cobrar, Cuenta del propietario y Situación financiera;
     señalado en NOTAS-P2 #11 y en cuentas-por-pagar.css): superficie de
     `.accounting-kpi-grid`/`.accounting-panel`/`.accounting-basis-banner`
     y el link `.account-link`/`.accounting-entry-link` (botón-texto sin
     dueño en boton.css). Tampoco el acordeón `.financial-entry-card` ni la
     tarjeta `.chart-account-group` (borde/fondo/padding/arreglo interno de
     su botón): sin componente que los reclame hoy — aquí solo se
     dimensionan las tablas y grillas que cuelgan de ellos.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Título arriba, selector de periodo abajo a la derecha (misma fusión que
     el resto de la familia contable: financial-operations-head + flow-head
     sobre un único section-head). */
  #view-accounting .section-head { align-items: flex-end; margin-bottom: 14px; }

  /* Libro mayor: selector de cuenta (2fr) + 4 cifras en fila, cada caja del
     mismo alto. */
  #view-accounting .accounting-ledger-head {
    display: grid;
    grid-template-columns: minmax(280px, 2fr) repeat(4, minmax(120px, 1fr));
    gap: 10px;
  }
  #view-accounting .accounting-ledger-head > label,
  #view-accounting .accounting-ledger-head > div { min-height: 68px; }

  /* Ancho mínimo compartido: dispara el scroll horizontal del wrap antes de
     que el navegador apriete columnas. */
  #view-accounting .accounting-table-wrap { overflow-x: auto; }
  #view-accounting .accounting-table { min-width: 850px; }

  /* Diario/mayor: 6 columnas, las últimas 3 son cifra (Debe/Haber/Saldo). */
  #view-accounting .accounting-ledger-table th:nth-last-child(-n+3),
  #view-accounting .accounting-ledger-table td:nth-last-child(-n+3) { text-align: right; }

  /* Balance: fila 1 son 4 celdas agrupadas (Cuenta + 3 colspan) centradas
     salvo Cuenta; fila 2 son las 6 etiquetas Debe/Haber sueltas; el cuerpo
     tiene 7 celdas (Cuenta + 6 de cifra). */
  #view-accounting .trial-balance-table thead tr:first-child th { text-align: center; }
  #view-accounting .trial-balance-table thead tr:first-child th:first-child { text-align: left; }
  #view-accounting .trial-balance-table thead tr:last-child th,
  #view-accounting .trial-balance-table tbody td:nth-child(n+2),
  #view-accounting .trial-balance-table tfoot td:nth-child(n+2) { text-align: right; }

  /* Celda Cuenta del balance: código+nombre no se aprieta contra las 6
     columnas de cifra que le siguen. */
  #view-accounting .account-link--row { min-width: 210px; }

  /* Líneas de un asiento (dentro del acordeón .financial-entry-card): Debe
     y Haber son las únicas columnas de cifra. */
  #view-accounting .financial-entry-lines-wrap { overflow-x: auto; }
  #view-accounting .financial-entry-list { display: grid; gap: 10px; }
  #view-accounting .financial-entry-lines { min-width: 680px; }
  #view-accounting .financial-entry-lines th:nth-child(n+3),
  #view-accounting .financial-entry-lines td:nth-child(n+3) { width: 150px; text-align: right; }

  /* Asiento manual: fecha/glosa/estado en fila; cada línea de movimiento es
     #, cuenta, detalle, debe, haber, quitar — ancho mínimo fuerza scroll en
     vez de apretar columnas. */
  #view-accounting .financial-journal-form { display: grid; gap: 12px; }
  #view-accounting .financial-journal-meta {
    display: grid;
    grid-template-columns: minmax(150px, .5fr) minmax(260px, 1.5fr) minmax(150px, .5fr);
    gap: 10px;
  }
  #view-accounting .financial-journal-lines { overflow-x: auto; }
  #view-accounting .financial-journal-line {
    display: grid;
    grid-template-columns: 28px minmax(230px, 1.1fr) minmax(220px, 1fr) 130px 130px 36px;
    gap: 6px;
    align-items: center;
    min-width: 850px;
  }
  #view-accounting .financial-journal-line--head {
    min-height: 32px; color: var(--muted); font-size: var(--fs-caption);
    font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  }
  #view-accounting .financial-journal-actions { display: flex; justify-content: flex-end; gap: 8px; }

  /* Plan de cuentas: tarjetas de a 3 por fila. */
  #view-accounting .chart-account-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
  }

  @media (max-width: 900px) {
    #view-accounting .accounting-ledger-head { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    #view-accounting .accounting-ledger-head > label { grid-column: 1 / -1; }
    #view-accounting .chart-account-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }

  @media (max-width: 680px) {
    #view-accounting .accounting-ledger-head,
    #view-accounting .chart-account-grid,
    #view-accounting .financial-journal-meta { grid-template-columns: 1fr; }
    #view-accounting .accounting-ledger-head > label { grid-column: auto; }
  }
}

/* líneas: 126 de presupuesto 150 */
/* ══ mes-a-mes — data-view="mom" ═════════════════════════════════════════════
   Cubre: #view-mom .reports-head (alineación de la cabecera con el selector
     de período + CSV), #view-mom .mom-dashboard (grilla de 2 columnas que
     aloja los 12 paneles: 2 gráficos + 4 tablas anchas + 6 tablas angostas),
     #view-mom .mom-span-full (panel que ocupa la fila completa),
     #view-mom .mom-chart-panel (+.report-panel-head/-actions: cabecera
     apilada porque el panel trae DOS segmentados, monto/mix/MoM/ratio y la
     franja horaria), #view-mom .mom-table (alto del contenedor de cada
     tabla/Tabulator).
   Fuente v1: styles.css ~L3606-3610 + ~L10397-10404 (.reports-head,
     fusionada con book/sales/recipe/benchmark-head), ~L6928-6979 (.mom-
     section/-dashboard/-chart-panel/-span-full), ~L7029-7045 (cabecera
     apilada del chart-panel + .mom-table), ~L10502 (.mom-dashboard a 1
     columna, 980px → mapeado a 900px).
   Casos límite: panel con más filas que sus 320px mínimos (ej. matriz de
     12 meses) crece libre — grid-auto-rows:minmax(320px,auto) deja que el
     vecino de la misma fila se estire parejo, no queda un hueco; los 6
     paneles angostos (mix/ticket/pago/mesero/comensales/salas) sí son
     número par: nunca queda uno solo ocupando la fila completa por accidente
     de layout, es a propósito por markup fijo. #momYearlyChart con una sola
     línea de ingreso en el <select> (fuente recién configurada, sin reglas)
     no cambia la geometría, es contenido del propio gráfico.
   NO cubre — gaps verificados sin regla en ningún 04-componentes hoy
     (2026-08-09), no se resuelven aquí por ser skin de componente, no
     geometría de esta vista (CONVENCIONES §8):
     · `.mom-hist-table`/`.mom-hist-wrap`/`.mom-hist-label`/`.mom-hist-pos/
       -neg/-zero/-null` — familia completa de tabla cruda (bordes, mayúsculas
       de cabecera, color por signo) que visten CASI todas las tablas de esta
       vista (matriz/mes-franja/interanual/mix/ticket/categoría/pago/mesero/
       comensales) y que además reutiliza tal cual el panel de Promociones
       (`.promo-behavior-table`, verificado en app.js L8764) — no es exclusiva
       de esta vista. Candidata natural: sumarla a `tabla.css`, familia de
       `.mom-matrix-total` (que ya vive ahí como fila-total).
     · `.mom-year-table` — pese al nombre, su único emisor real es
       `#roomsTable` en `view-rooms` (Salas · ocupación), no esta vista
       (verificado en app.js L6190); queda para esa vista, no para mes-a-mes.
     · `.mom-year-panel` — clase con regla en v1 pero sin emisor en index.php
       ni app.js para esta vista (verificado); muerta, no se porta.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  .mom-section { margin-top: 0; }
  .mom-rules-hint { margin-top: 12px; font-size: var(--fs-small); line-height: 1.5; }

  /* Título arriba a la izquierda: el período + CSV pueden envolver a dos
     líneas y no deben tirar el título al centro vertical. */
  #view-mom .reports-head { align-items: flex-start; }

  /* Dos columnas parejas; los paneles angostos comparten fila y se estiran
     al alto del más alto (default align-items:stretch de grid). */
  #view-mom .mom-dashboard {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-auto-rows: minmax(320px, auto);
    gap: 10px;
  }

  /* Gráficos y tablas anchas (matriz, mes-franja, interanual, categoría)
     rompen las dos columnas y toman la fila entera. */
  #view-mom .mom-span-full { grid-column: 1 / -1; }

  /* Los paneles de tendencia necesitan más alto que un panel de tabla:
     traen eje + leyenda + zoom, no solo filas. */
  #view-mom .mom-chart-panel { min-height: 460px; }

  /* Cabecera apilada (título arriba, controles abajo a lo ancho): un panel
     de gráfico trae DOS segmentados (tipo de vista + franja horaria) que no
     caben junto al título en una sola fila sin apretar. */
  #view-mom .mom-chart-panel .report-panel-head {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }
  #view-mom .mom-chart-panel .report-panel-actions {
    width: 100%;
    justify-content: flex-start;
    flex-wrap: wrap;
    gap: 6px;
  }

  /* Contenedor de cada tabla (HTML crudo o Tabulator): llena la segunda
     fila del .report-panel (minmax(0,1fr)) con un piso legible aun vacío. */
  #view-mom .mom-table {
    min-height: 240px;
    height: 100%;
  }

  /* ── Responsive ──────────────────────────────────────────────────────── */
  @media (max-width: 900px) {
    /* v1 colapsaba en 980px: se apila 1 columna, cada panel a lo ancho. */
    #view-mom .mom-dashboard { grid-template-columns: 1fr; }
  }
}

/* líneas: 93 de presupuesto 150 */
/* ══ pedidos (vista) ═════════════════════════════════════════════════════
   Vista: "Pedidos a proveedores" · #view-orders / data-view="orders"
   Cubre: .orders-head, .kitchen-requests-layout(+-list-panel/-detail-panel),
     .kitchen-requests-wrap(+tr.is-selected), .kitchen-request-detail-panel,
     .kitchen-request-detail-head, .kitchen-request-note, .kitchen-linked-orders,
     .kitchen-request-lines/-line(.is-allocated), .kitchen-prepare-groups,
     .orders-compose-grid, .orders-catalog-panel/.orders-cart-panel,
     .orders-form-row, .order-supplier-meta, .orders-product-search,
     .order-product-results, .order-product-card(.is-added), .orders-cart-wrap,
     .orders-cart-table(+1ª col), .order-line-source/-input/-unit/-price,
     .orders-notes, .order-rule-warning, .orders-total-row, .orders-actions,
     .orders-send-help, .orders-summary, .orders-history-wrap,
     .orders-suppliers-wrap, .orders-restrictions-cell, .order-supplier-form-grid
     (solo margen — las 3 columnas ya las da .modal-grid), .orders-import-file,
     .orders-import-preview (.table-wrap/table/select)
   Fuente v1: styles.css L14475-14685 — bloque único y contiguo, sin
     apariciones duplicadas en otras capas del archivo (nada que fusionar).
   NO cubre — ya resuelto en 04-componentes, no re-skinear aquí:
     .orders-tabs → toolbar.css (receta .segmented-control, ya incluye
     width:fit-content + margin); .order-status/.status-chip → badge.css;
     .kitchen-email-notice → feedback.css; anchos de .order-supplier-modal/
     .order-import-modal/.order-confirm-modal y su scroll interno →
     modal.css; .check-line del form proveedor → campo.css; grilla de 3
     columnas del form proveedor → .modal-grid (modal.css, colapsa a 1fr en
     900px — reemplaza el quiebre a 720px de v1, mismo resultado visual).
   Gaps detectados, fuera de esta vista (no se resuelven aquí):
     · .tool-panel (superficie de .orders-catalog-panel y de
       .kitchen-request-detail-panel) no tiene receta en 04-componentes/
       panel.css — se usa en ≥8 vistas más (reconciliación, coffee, daily,
       compras-provider, RRHH…), así que es un faltante de panel.css, no
       algo para resolver por vista.
     · .orders-whatsapp-action recolorea .primary-action a verde (=
       var(--green) exacto) — es variante de BOTÓN, no geometría; falta
       nombrarla en 04-componentes/boton.css (ej. .primary-action.success).
       Prohibido re-skinear el botón desde aquí.
   Estados: tarjetas/placas de esta vista son de solo lectura (sin
     hover/focus propio, heredan el de sus botones/inputs internos);
     .order-product-card.is-added y .kitchen-request-line.is-allocated son
     el único estado visual (marcado por JS, sin transición: cambian con el
     re-render). Vacío = .empty de feedback.css (ya resuelto).
   Casos límite: nombre de producto/solicitud largo → ellipsis (tarjetas,
     celdas); solicitud sin proveedor sugerido → tarjeta "Sin proveedor
     sugerido" sin geometría especial; import sin archivo → contenedor sin
     alto (root.innerHTML vacío).
   ═══════════════════════════════════════════════════════════════════════ */

@layer vistas {

  .orders-panel[hidden] { display: none; }
  .orders-portal-link { text-decoration: none; }

  .orders-head { margin-bottom: 12px; }

  /* ── Solicitudes Kitchen: lista + detalle ─────────────────────────────── */
  .kitchen-requests-layout {
    display: grid;
    grid-template-columns: minmax(560px, 1.08fr) minmax(390px, .92fr);
    gap: 12px;
    align-items: start;
  }
  .kitchen-requests-list-panel,
  .kitchen-request-detail-panel { margin-top: 0; }
  .kitchen-requests-wrap { max-height: calc(100dvh - var(--nav-alto) - 374px); }
  .kitchen-requests-wrap tr.is-selected td {
    background: color-mix(in srgb, var(--green) 5%, var(--surface));
  }
  .kitchen-request-detail-panel { max-height: calc(100dvh - var(--nav-alto) - 294px); overflow: auto; }
  .kitchen-request-detail-head { margin-bottom: 10px; }

  /* Nota libre de la solicitud: cita con filete de marca */
  .kitchen-request-note {
    margin: 8px 0 12px;
    padding: 10px 12px;
    border-left: 3px solid var(--green);
    border-radius: 0 var(--r-xs) var(--r-xs) 0;
    background: var(--surface-2);
    color: var(--ink-2);
    font-size: var(--fs-body);
    line-height: 1.5;
  }

  .kitchen-linked-orders { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 10px 0 14px; }
  .kitchen-linked-orders > strong {
    width: 100%; color: var(--muted); font-size: var(--fs-caption);
    font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
  }

  .kitchen-request-lines { display: grid; gap: 8px; }
  .kitchen-request-line {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(160px, .72fr);
    gap: 12px;
    align-items: center;
    padding: 10px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
  }
  .kitchen-request-line.is-allocated { opacity: .67; }   /* completa: se atenúa, no desaparece */
  .kitchen-request-line > div { display: grid; gap: 4px; min-width: 0; }
  .kitchen-request-line strong { overflow: hidden; text-overflow: ellipsis; }

  .kitchen-prepare-groups { display: grid; gap: 8px; margin-top: 14px; }
  /* Adaptación contextual del botón compartido: rótulo a la izquierda, contador a la derecha */
  .kitchen-prepare-groups .secondary-action { justify-content: space-between; gap: 12px; text-align: left; }
  .kitchen-prepare-groups .secondary-action span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* ── Armar pedido: catálogo del proveedor + carrito ───────────────────── */
  .orders-compose-grid {
    display: grid;
    grid-template-columns: minmax(340px, .82fr) minmax(560px, 1.18fr);
    gap: 12px;
    align-items: start;
  }
  .orders-catalog-panel,
  .orders-cart-panel { margin-top: 0; }

  .orders-form-row { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(150px, .6fr); gap: 10px; }

  /* Condiciones del proveedor elegido: misma fórmula de tinte que badge.css */
  .order-supplier-meta {
    display: grid;
    gap: 6px;
    min-height: 86px;
    margin: 12px 0;
    padding: 12px 14px;
    border: 1px solid color-mix(in srgb, var(--green) 24%, transparent);
    border-radius: var(--r);
    background: color-mix(in srgb, var(--green) 6%, var(--surface));
  }
  .order-supplier-meta > div { display: flex; justify-content: space-between; gap: 10px; }

  .orders-product-search { margin-bottom: 8px; }
  .order-product-results {
    display: grid;
    gap: 8px;
    max-height: calc(100dvh - var(--nav-alto) - 454px);
    min-height: 128px;
    overflow: auto;
    padding-right: 4px;
  }
  .order-product-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
  }
  .order-product-card > div { min-width: 0; display: grid; gap: 2px; }
  .order-product-card small,
  .order-product-card span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .order-product-card.is-added {
    border-color: color-mix(in srgb, var(--green) 30%, transparent);
    background: color-mix(in srgb, var(--green) 4%, var(--surface));
  }

  .orders-cart-wrap { max-height: calc(100dvh - var(--nav-alto) - 454px); min-height: 180px; }
  .orders-cart-table { min-width: 720px; }
  .orders-cart-table th:nth-child(1) { width: 38%; }
  .order-line-source { display: block; margin-top: 4px; color: var(--muted); font-size: var(--fs-caption); font-weight: 550; }
  .order-line-input { width: 88px; min-width: 72px; height: var(--control-h-compact); padding: 6px 8px; }
  .order-line-unit { width: 80px; }
  .order-line-price { width: 105px; }   /* alineación a la derecha: hereda de su <td class="numeric"> */

  .orders-notes { margin-top: 12px; }

  .order-rule-warning {
    margin-top: 10px;
    padding: 10px 12px;
    border: 1px solid color-mix(in srgb, var(--amber) 25%, transparent);
    border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--amber) 7%, var(--surface));
    color: var(--amber);
    font-size: var(--fs-body);
    font-weight: 650;
  }

  .orders-total-row {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 20px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line);
  }
  .orders-total-row span { color: var(--muted); font-size: var(--fs-small); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
  .orders-total-row strong { color: var(--ink); font-size: var(--fs-h2); }

  .orders-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-top: 12px; }
  .orders-send-help { margin: 8px 0 0; color: var(--muted); font-size: var(--fs-caption); line-height: 1.45; text-align: right; }

  /* ── Historial / Proveedores: resumen + tablas ────────────────────────── */
  .orders-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
  .orders-summary > div {
    display: grid; gap: 6px; padding: 14px 16px;
    border: 1px solid var(--line); border-radius: var(--r);
    background: var(--surface); box-shadow: var(--shadow-xs);
  }
  .orders-summary span { color: var(--muted); font-size: var(--fs-caption); font-weight: 650; text-transform: uppercase; letter-spacing: .04em; }
  .orders-summary strong { color: var(--ink); font-size: var(--fs-h2); }

  .orders-history-wrap { max-height: calc(100dvh - var(--nav-alto) - 314px); }
  .orders-suppliers-wrap { max-height: calc(100dvh - var(--nav-alto) - 294px); }
  .orders-restrictions-cell { min-width: 260px; max-width: 420px; }

  /* ── Modal proveedor: la grilla de 3 columnas ya la da .modal-grid ───────
     (colapsa sola a 900px); acá solo el respiro bajo el selector superior. */
  .order-supplier-form-grid { margin-top: 14px; }

  /* ── Modal importar proveedores ────────────────────────────────────────── */
  .orders-import-file { margin: 16px 0 10px; }
  .orders-import-preview .table-wrap { max-height: calc(100dvh - var(--nav-alto) - 314px); }
  .orders-import-preview table { min-width: 980px; }
  .orders-import-preview select { min-width: 330px; }

  /* ── Responsive: 1100 apila las dos grillas de dos columnas; 680 apila
     formulario/resumen/línea de solicitud (.orders-tabs ya lo hace toolbar.css) ── */
  @media (max-width: 1100px) {
    .kitchen-requests-layout { grid-template-columns: 1fr; }
    .kitchen-request-detail-panel { max-height: none; }
    .orders-compose-grid { grid-template-columns: 1fr; }
    .order-product-results { max-height: 360px; }
    .orders-cart-wrap { max-height: none; }
  }
  @media (max-width: 680px) {
    .orders-form-row,
    .orders-summary { grid-template-columns: 1fr; }
    .kitchen-request-line { grid-template-columns: 1fr; }
    .orders-actions { justify-content: stretch; }
    .orders-actions button { flex: 1 1 150px; }
  }
}

/* líneas: ~236 de presupuesto 150 — vista con 4 pestañas + 3 modales;
   excede por cobertura real de placas/tarjetas propias sin dueño en
   04-componentes (order-supplier-meta, order-product-card, order-rule-
   warning, kitchen-request-note/-line, orders-summary): ningún componente
   las reclama y no se reusan fuera de esta vista. Ver gaps documentados
   arriba (.tool-panel, .orders-whatsapp-action) para lo que sí falta subir
   a 04-componentes. */
/* ══ proyeccion — data-view="forecast" ═══════════════════════════════════════
   Cubre: #view-forecast .overview-dashboard, #view-forecast .overview-grid
     (2 columnas asimétricas), #view-forecast .overview-grid >
     .overview-panel--wide:last-child (grid-column: panel.css delega esta
     decisión a la vista dueña de la grilla — ver su comentario en
     .finance-overview-panel), #view-forecast .overview-chart(+--main),
     #view-forecast .forecast-table (+-backtest-head/-table-grid: tablas
     inyectadas por JS).
   Fuente v1: styles.css ~L4340-4516 (overview-dashboard/-grid/-chart,
     fusión con sus breakpoints ~L10261 y ~L10924-10928), ~L5843-5850
     (forecast-backtest-head + forecast-table-grid). Verificado en vivo
     (localhost:8090, admin/1234): v1 nunca declaró grid-column para --wide
     fuera de .dashboard-grid/.finance-overview-grid.
   Casos límite: sin historia suficiente → los 3 slots pintan .empty (ya
     vestido en feedback.css, sin regla aquí); sin backtest → JS omite el
     bloque "Proyección vs real" completo (no hay estado CSS que cubrir);
     canales dinámicos (2 a N) en la tabla de detalle → overflow-x scrollea
     en vez de romper el panel, igual que .accounting-table-wrap.
   Nota — fix visible sobre v1, verificado con captura antes/después: las 3
     secciones (Total/Composición/Detalle) comparten la clase idéntica
     .overview-panel--wide, pero v1 solo le da min-height (panel.css) sin
     grid-column — el auto-placement pone Total+Composición lado a lado
     (2 items llenan las 2 columnas, esto SÍ es intencional, confirmado en
     vivo) y Detalle queda solo en la 1ª columna con un hueco vacío del
     ancho de la 2ª a su derecha (esto NO es intencional). :last-child
     aísla solo a Detalle sin romper el paso lado-a-lado de los otros dos
     — aplicar :wide a los 3 (como hace .dashboard-grid) los apilaría a
     todos, verificado que rompe el layout real. Vistas hermanas con el
     mismo patrón (#view-overview, #view-rooms) deben replicar esta regla
     scopeada a su propio id, verificando primero si en su caso TODAS las
     secciones deben apilarse o si aplica el mismo caso de "última angosta".
   Nota — 18px→20px: .forecast-backtest-head + .forecast-table-grid usaba
     un margen fuera de la escala canon; se redondea al valor más cercano.
   NO cubre — gap de componente, no de esta vista (verificado sin regla en
     ningún 04-componentes hoy): .widget-pin(--corner)/.has-widget-pin/
     .is-pinned, el botón "+"/"✓" que ancla cualquier panel al Resumen
     (styles.css ~L14388-14417). Es skin+posición de esquina con sus propios
     estados (reposo/hover/is-pinned), inyectado por JS sobre CUALQUIER
     .overview-panel/.report-panel/.table-section de CUALQUIER vista con
     widgets anclables (Proyección, Salas, Reportes, Menú, MoM…) — candidato
     natural a panel.css (ya dueño de .overview-panel). No se resuelve aquí
     por CONVENCIONES §8.
   ═══════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Estructura: barra de mando + KPIs + grilla de paneles apilados ──── */
  #view-forecast .overview-dashboard { display: grid; gap: 14px; }

  /* 2 columnas asimétricas: el total pesa más que la composición por canal
     (confirmado en vivo, no es una proporción arbitraria). */
  #view-forecast .overview-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(320px, .85fr);
    align-items: stretch;
    gap: 12px;
  }

  /* Solo el último panel (Detalle) pasa a fila completa — Total y
     Composición SÍ deben quedar lado a lado (ver Nota arriba). */
  #view-forecast .overview-grid > .overview-panel--wide:last-child { grid-column: 1 / -1; }

  /* ── Lienzo de gráfico: ECharts monta su canvas/svg adentro ──────────── */
  #view-forecast .overview-chart { min-width: 0; flex: 1; min-height: 268px; }
  #view-forecast .overview-chart--main { height: 330px; }

  /* ── Detalle: 1-2 <table class="data-table forecast-table-grid"> según
     haya o no meses cerrados con backtest; look de tabla ya lo da la
     receta canon, aquí solo el ritmo entre bloques. ─────────────────── */
  #view-forecast .forecast-table { overflow-x: auto; }
  #view-forecast .forecast-backtest-head { margin-bottom: 8px; font-size: var(--fs-body); }
  #view-forecast .forecast-backtest-head + .forecast-table-grid { margin-bottom: 20px; }

  @media (max-width: 1100px) {
    #view-forecast .overview-grid { grid-template-columns: 1fr; }
  }

  @media (max-width: 900px) {
    #view-forecast .overview-chart--main { height: 280px; min-height: 260px; }
  }
}

/* líneas: ~82 de presupuesto 150 */
/* ══ reglas — data-view="rules" ═════════════════════════════════════════════
   Cubre: .learned-rules-table (+ anchos th:nth-child 1-8), .rule-factor-grid,
     .rule-risk-stack, .learned-rule-catalog, y las variantes de columna de
     .learned-rules-table sobre .table-inline-field/.table-action-row/
     .table-action (componentes genéricos, geometría acotada a esta tabla).
   Fuente v1: styles.css ~L6229-6325 (única aparición; sin capas duplicadas
     v117/v118/v150 para esta familia).
   Estados: sin estados propios — es geometría de columnas/celdas. Los
     estados de sus controles (input inline, botón Guardar/Eliminar, pill de
     riesgo) viven en campo.css/boton.css/badge.css.
   Casos límite: proveedor/insumo con nombre largo → overflow-wrap:anywhere
     dentro del ancho fijo de columna (no ellipsis: el RUT/unidad de abajo
     debe seguir legible); sin factor/effective → texto plano "auto" (JS);
     tabla no colapsa bajo 1060px, scrollea dentro de .table-wrap heredado
     (no se agrega breakpoint propio: v1 tampoco lo tenía). No hay panel de
     detalle separado: v1 edita cada regla in-place en su fila (celda 4 y 5
     con inputs), no hay vista maestro-detalle en el DOM real.
   NO cubre — gap fuera de este archivo: .rule-risk/.rule-risk--ok/warn/bad
     (el pill de riesgo en sí, celda 3) no tiene regla en ningún componente
     v2 hoy (verificado: cero ocurrencias de "risk" en src/); es una familia
     de badge.css (mismo patrón que .reco-badge.ok/warn/bad) que falta
     agregar ahí — no se resuelve aquí porque sería re-skinear un badge desde
     la vista. .learned-rules-section/.learned-rules-wrap son marcadores sin
     CSS propio en v1: heredan .table-section/.table-wrap tal cual.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Tabla ancha de 8 columnas con celdas multilínea (dato + metadatos
     apilados): layout fijo para que los anchos no salten al filtrar. */
  .learned-rules-table {
    min-width: 1060px;
    table-layout: fixed;
  }

  .learned-rules-table th:nth-child(1) { width: 13%; }  /* Proveedor */
  .learned-rules-table th:nth-child(2) { width: 12%; }  /* Nombre XML */
  .learned-rules-table th:nth-child(3) { width: 12%; }  /* Riesgo */
  .learned-rules-table th:nth-child(4) { width: 18%; }  /* Insumo destino */
  .learned-rules-table th:nth-child(5) { width: 14%; }  /* Unidad / factor */
  .learned-rules-table th:nth-child(6) { width: 13%; }  /* Uso histórico */
  .learned-rules-table th:nth-child(7) { width: 9%; }   /* Precio */
  .learned-rules-table th:nth-child(8) { width: 9%; }   /* Acciones */

  /* La base centra verticalmente; aquí cada <td> apila strong+small y debe
     alinear arriba, no al medio del bloque. */
  .learned-rules-table td {
    vertical-align: top;
    overflow-wrap: anywhere;
  }

  /* Col. 4 — el campo de catálogo llena el ancho completo de la celda
     (108px de mínimo del componente se queda corto en un 18% de columna). */
  .learned-rule-catalog,
  .learned-rules-table .table-inline-field {
    width: 100%;
    min-width: 0;
  }

  /* Col. 5 — unidad y factor de conversión lado a lado dentro del 14%. */
  .rule-factor-grid {
    display: grid;
    grid-template-columns: minmax(46px, .85fr) minmax(64px, 1fr);
    gap: 6px;
    align-items: center;
  }

  /* Col. 3 — una regla puede traer varias etiquetas de riesgo a la vez. */
  .rule-risk-stack {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 5px;
    max-width: 190px;
  }

  /* Col. 8 (9% de ancho) — Guardar/Eliminar apilados, nunca en línea: el
     componente genérico los pone en fila, aquí no entrarían. */
  .learned-rules-table .table-action-row {
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
  }
  .learned-rules-table .table-action { white-space: nowrap; }
}

/* líneas: 87 de presupuesto 150 */
/* ══ rentabilidad-carta — #view-menu (data-view="menu") ═══════════════════
   Cubre: .menu-section, .menu-filter-strip, .menu-exclusion-control(+-chips),
     .menu-dashboard(+-chart-panel/-side-panel/-quadrant-panel),
     .menu-opportunity-list, .menu-quadrant-list, .menu-recommendations(-panel),
     .menu-wrap, .menu-table, .menu-row--excluded,
     #view-menu .menu-table .item-cell.
   Fuente v1: styles.css ~L2124-2260 (sección, filtros, exclusión, dashboard,
     listas), ~L2295-2303 (panel de recomendaciones), ~L2587-2608 (tabla de
     detalle), ~L10217-10281 (responsive 1180 → consolidado a 1100, el mismo
     punto donde panel.css ya reduce `.report-chart.menu-chart` a 420px).
   Casos límite: nombre de producto largo en la 1ª columna → item-cell le
     da ancho antes que la fila lo apriete; sin focos/cuadrantes para el
     filtro → `.empty` (feedback.css) que pinta app.js, sin fila fantasma
     aquí; recomendaciones sin generar → panel con `hidden` (reset), no
     ocupa alto; dato ausente llega como "-" de texto plano desde el JS.
   Decisiones de porte: `.menu-section` pierde la tarjeta de `.table-section`
     (mismo patrón que `.daily-work-section` en tareas-diarias.css) porque es
     un stack transparente de tarjetas propias, no una tarjeta ella misma;
     el `min-width:0` en sus hijos evita que la tabla ancha (1900px) fuerce
     el track de la grilla. `.report-chart.menu-chart` (alto 460/420) NO se
     toca aquí: ya vive completo en panel.css.
   Gaps detectados, fuera de esta vista (no se resuelven aquí):
     · `.report-board-head` — misma superficie que `.report-panel` (borde+
       fondo+radio+padding) pero sin dueño en 04-componentes; se deja como
       `.section-head` desnudo hasta que se suba (afecta también a una
       futura vista "Reportes" que comparte la clase).
     · `.menu-opportunity`, `.menu-quadrant-card`(+6 variantes de color),
       `.menu-recommendation-*`(+4 variantes) y `.menu-exclusion-chip` son
       familias de tarjeta/badge con color propio sin entrada en badge.css
       ni panel.css (mismo caso que `.coffee-quote-type--*` en eventos.css:
       "sin entrada en badge.css"). Aquí solo se define la geometría de sus
       listas contenedoras (grid+gap); el color/tipografía de cada tarjeta
       queda pendiente de subir a componentes.
   ═══════════════════════════════════════════════════════════════════════ */

@layer vistas {

  .menu-table-section,
  .menu-section > *,
  .menu-dashboard > * { min-width: 0; }
  .menu-kpis { margin-top: 0; }
  .menu-exclusion-empty { color: var(--muted-2); font-size: var(--fs-caption); font-weight: 650; }
  .menu-recommendation-columns {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px;
  }
  .menu-recommendation-columns div { min-width: 0; display: grid; gap: 3px; }
  .menu-recommendation-columns b { color: var(--ink-2); font-size: var(--fs-caption); }
  .menu-recommendation-columns small { color: var(--muted); font-size: var(--fs-caption); line-height: 1.3; }

  /* ── Sección raíz: stack transparente, no tarjeta ─────────────────────── */
  .menu-section {
    margin-top: 0;
    padding: 0;
    border: 0;
    background: transparent;
    display: grid;
    gap: 8px;
  }
  .menu-section > *,
  .menu-dashboard > * { min-width: 0; }

  /* ── Filtros: buscador / categoría / cuadrante / exclusión ────────────── */
  .menu-filter-strip {
    display: grid;
    grid-template-columns: minmax(220px, 1fr) minmax(150px, 220px) minmax(170px, 220px) minmax(260px, 1fr);
  }

  /* Select "Excluir del cálculo" + botón "Limpiar"; los chips ocupan la
     fila completa debajo (grid-column 1/-1) para no forzar el ancho. */
  .menu-exclusion-control {
    display: grid;
    grid-template-columns: minmax(180px, 1fr) auto;
    gap: 8px;
    align-items: end;
  }
  .menu-exclusion-control label { min-width: 0; }
  .menu-exclusion-chips {
    grid-column: 1 / -1;
    min-height: 24px;   /* reserva alto: no salta al pasar de vacío a con chips */
    display: flex;
    align-items: center;
    gap: 6px;
    overflow-x: auto;
  }

  /* ── Cuadrante: matriz a la izquierda, focos + resumen a la derecha ────
     align-self:start en el panel de chart es lo que evita que se estire al
     alto combinado de side+quadrants (columna derecha ~578px vs chart
     ~540px de contenido real). */
  .menu-dashboard {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(260px, 340px);
    grid-template-areas: "chart side" "chart quadrants";
    gap: 8px;
  }
  .menu-chart-panel { grid-area: chart; min-height: 520px; align-self: start; }
  .menu-side-panel { grid-area: side; min-height: 300px; }
  .menu-quadrant-panel { grid-area: quadrants; min-height: 270px; }

  /* Focos por producto y resumen de cuadrantes: misma receta de lista */
  .menu-opportunity-list,
  .menu-quadrant-list { display: grid; gap: 8px; align-content: start; }

  /* ── Recomendaciones de IA: tarjetas fluidas, respiro extra sobre el
     gap de la sección (es contenido inyectado, no parte fija del stack) ── */
  .menu-recommendations-panel { margin-top: 8px; }
  .menu-recommendations {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(360px, 100%), 1fr));
    gap: 10px;
  }

  /* ── Tabla de detalle: 11 columnas, table-layout fijo para que el
     ellipsis funcione sin medir contenido ──────────────────────────────── */
  .menu-wrap { max-height: 520px; }
  /* Sin `table-layout: fixed`: con ancho fijo el navegador reparte las 14
     columnas por igual y estrangula las cortas — la de "Resultado" cortaba
     su badge (verificado: sobraban 9 px). Con reparto automático dentro del
     min-width, cada columna toma lo que su contenido pide. */
  .menu-table { min-width: 1900px; }
  .menu-table th,
  .menu-table td { overflow: hidden; text-overflow: ellipsis; }
  .menu-table .numeric { white-space: nowrap; }
  #view-menu .menu-table .item-cell { min-width: 230px; }   /* .item-cell es compartida entre vistas */

  /* Productos excluidos siguen visibles para poder reingresarlos, pero no
     deben competir visualmente con los que participan del cálculo. */
  .menu-row--excluded td {
    background: color-mix(in srgb, var(--amber) 5%, var(--surface));
    color: var(--muted);
  }
  .menu-row--excluded:hover td {
    background: color-mix(in srgb, var(--amber) 8%, var(--surface));
  }

  /* ── Responsive ────────────────────────────────────────────────────────
     1100: mismo punto donde panel.css ya baja el chart a 420px — el
     cuadrante se apila en 3 filas y los paneles sueltan su piso mínimo. */
  @media (max-width: 1100px) {
    .menu-dashboard {
      grid-template-columns: 1fr;
      grid-template-areas: "chart" "side" "quadrants";
    }
    .menu-chart-panel,
    .menu-side-panel,
    .menu-quadrant-panel { min-height: 0; }
  }

  @media (max-width: 900px) {
    .menu-filter-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }

  @media (max-width: 680px) {
    .menu-filter-strip,
    .menu-exclusion-control { grid-template-columns: 1fr; }
    .menu-recommendation-columns { grid-template-columns: 1fr; }
  }
}

/* líneas: 132 de presupuesto 150 */
/* ══ resumen-financiero — data-view="finance-overview" ══════════════════════
   Cubre: #view-finance-overview .finance-overview-period-row,
     #view-finance-overview .finance-overview-grid (+ .finance-overview-panel--wide),
     #view-finance-overview .finance-quick-actions.
   Fuente v1: styles.css ~L9660-9676 (grid dos columnas + --wide),
     ~L9762-9768 (quick-actions), ~L15850-15851 (period-row) — fusionadas con
     el responsive ~L10190-10203 (720) y ~L16126-16129 (760), consolidado al
     breakpoint canon 680.
   Casos límite: título/mes con texto largo — no aplica (rótulo fijo "Resumen
     financiero" + mes calendario, ambos acotados); grilla con menos de 4
     paneles (p.ej. mientras el JS arma el render) — el grid no exige conteo
     fijo, cada .finance-overview-panel ocupa la celda que le toque llegar.
   NO cubre — skin compartido con vistas hermanas, sin dueño en 04-componentes
     hoy (verificado por grep 2026-08-09); no se resuelve por decreto de una
     sola vista (CONVENCIONES §8):
     · `.finance-period-banner`(+`-main`)/`.finance-source-coverage` — banner
       de estado del periodo (degradé + badge) que emite
       financeStatusBannerMarkup() y reutiliza igual el Estado de resultados
       (`flow`, misma función).
     · `.finance-summary-list` — lista dt/dd con filete superior; la comparte
       el panel "Controles" de Flujo de caja (`cashflow`).
     · `.finance-reading-grid` — grilla de 4 tarjetas info; familia de
       `.finance-support-grid` (Estado de resultados) y `.cashflow-kpi-grid`
       (Flujo de caja) — candidata al `:is()` de kpi.css, que hoy está en su
       presupuesto exacto (300/300): decidir en P4 si se amplía o se separa.
   `.finance-overview-shell` no tiene regla propia: su único CSS en v1 era
     `width:100%`, redundante con el bloque por defecto de una `<section>`.
   `.finance-position-summary` tampoco: hook sin estilo propio en v1, viaja
     siempre junto a --wide (panel "Solvencia"), que sí resuelve la grilla.
   `.finance-period-tools` tampoco: la fila ya la resuelve `.book-tools`
     (toolbar.css) + `.financial-close-month-picker` (page-header.css) +
     `.icon-button` (boton.css) sin ajuste adicional — mismo combo que usan
     Situación financiera y Cuentas por pagar.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Título + selector de mes al ras, extremos opuestos. */
  #view-finance-overview .finance-overview-period-row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 18px;
    margin-bottom: 16px;
  }

  /* Dos columnas iguales; "Solvencia" y "Lectura ejecutiva" llegan con
     --wide y ocupan la fila completa (grid-column es geometría de ESTA
     grilla, no del componente — así lo deja dicho panel.css). */
  #view-finance-overview .finance-overview-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
  }
  #view-finance-overview .finance-overview-panel--wide { grid-column: 1 / -1; }

  /* Accesos rápidos a las vistas relacionadas: fila al ras derecho; con
     8 botones es la primera zona de la vista en pedir wrap. */
  #view-finance-overview .finance-quick-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 16px;
  }

  @media (max-width: 680px) {
    /* Título arriba, selector de mes abajo a lo ancho — v1 quebraba en 760. */
    #view-finance-overview .finance-overview-period-row {
      flex-direction: column;
      align-items: flex-start;
      gap: 12px;
    }

    /* Una columna; --wide deja de significar nada pero se explicita por
       claridad (v1 lo reseteaba igual en su quiebre de 720). */
    #view-finance-overview .finance-overview-grid { grid-template-columns: 1fr; }
    #view-finance-overview .finance-overview-panel--wide { grid-column: auto; }

    /* Botones a ancho completo, uno debajo del otro. */
    #view-finance-overview .finance-quick-actions > .secondary-action { flex: 1 1 100%; }
  }
}

/* líneas: ~85 de presupuesto 150 */
/* ══ resumen configurable — data-view="overview" ══════════════════════════
   Cubre: #view-overview .overview-dashboard, .dashboard-grid,
     .dashboard-loading, .dashboard-widget-body, variantes de widget y
     .dashboard-widget-controls. El vacío y los pines viven en
     feedback.css/panel.css respectivamente.
   Fuente v1: styles.css ~L14337-14417, fusionada con overview base.
   Casos límite: cero widgets → dashboard-empty; carga/error por widget →
     .empty; tabla/lista larga → scroll interno, sin estirar todo el Resumen.
   ═════════════════════════════════════════════════════════════════════════ */

@layer vistas {
  #view-overview .overview-dashboard { display: grid; gap: 14px; }

  #view-overview .dashboard-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
  }
  #view-overview .dashboard-grid .overview-panel--wide { grid-column: 1 / -1; }
  #view-overview .dashboard-loading { grid-column: 1 / -1; }

  .dashboard-widget-body { flex: 1; min-width: 0; min-height: 0; }
  .dashboard-widget--chart .dashboard-widget-body { height: 320px; }
  :is(.dashboard-widget--table, .dashboard-widget--list, .dashboard-widget--cards) .dashboard-widget-body {
    max-height: 360px;
    overflow: auto;
  }
  .dashboard-widget-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
  }
  .dashboard-widget-controls .report-chart-tabs { margin: 0; }

  @media (max-width: 1100px) {
    #view-overview .dashboard-grid { grid-template-columns: 1fr; }
  }
}

/* líneas: ~42 de presupuesto 150 */
/* ══ salas — data-view="rooms" ════════════════════════════════════════════
   Cubre: #view-rooms .overview-grid, .rooms-occ-bar, .mom-year-table,
     .rooms-heatmap-wrap/.rooms-heatmap. Paneles, KPIs y gráficos reutilizan
     panel.css/kpi.css.
   Fuente v1: styles.css ~L6982-7025 + geometría overview.
   Casos límite: sin salas/franjas → .empty; porcentaje ausente → “—”;
     zoom o muchas columnas → scroll local.
   ═════════════════════════════════════════════════════════════════════════ */

@layer vistas {
  #view-rooms .overview-dashboard { display: grid; gap: 14px; }
  #view-rooms .overview-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
  #view-rooms .overview-panel--wide { grid-column: 1 / -1; }
  #view-rooms .overview-chart--main { min-height: 430px; }
  #view-rooms .forecast-table,
  #view-rooms .rooms-heatmap-wrap { overflow-x: auto; }

  .mom-year-table { width: 100%; min-width: 680px; border-collapse: collapse; }
  .mom-year-table th,
  .mom-year-table td { padding: 10px 12px; border-bottom: 1px solid var(--line); }
  .mom-year-table th { background: var(--surface-2); color: var(--muted); font-size: var(--fs-caption); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
  .mom-year-table td { color: var(--ink-2); font-size: var(--fs-small); }

  .rooms-occ-bar {
    position: relative;
    display: inline-flex;
    justify-content: flex-end;
    min-width: 120px;
    padding: 4px 8px;
    overflow: hidden;
    border-radius: 999px;
    background: var(--surface-3);
  }
  .rooms-occ-bar::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--w, 0%);
    background: color-mix(in srgb, var(--accent) 24%, var(--surface));
  }
  .rooms-occ-bar b { position: relative; font-weight: 700; }

  .rooms-heatmap { width: 100%; min-width: 620px; border-collapse: separate; border-spacing: 4px; }
  .rooms-heatmap th { padding: 6px; color: var(--muted); font-size: var(--fs-caption); font-weight: 700; text-align: center; }
  .rooms-heatmap th:first-child { text-align: left; white-space: nowrap; }
  .rooms-heatmap td {
    height: 54px;
    min-width: 74px;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--accent) var(--heat, 0%), var(--surface));
    color: var(--ink);
    text-align: center;
  }
  .rooms-heatmap td b { font-family: var(--font-numeric); font-size: var(--fs-body); }
  .rooms-heatmap-wrap .section-note { margin-top: 10px; }
}

/* líneas: ~72 de presupuesto 150 */
/* ══ simulador-promos — #view-promotions (data-view="promotions") ══════════
   Cubre: .promo-scope-row/-slot, .promo-dd(-panel) (disclosure de filtro,
     reusado por categorías-a-estudiar y excluidos), .promo-map(-head/
     -lenses/-body/-stage/-zone(.tl/.tr/.bl/.br)/-axis(.axis-x/.axis-y)/
     -legend(.has-categories)/-legend-title/-sep/.promo-legend-shape
     (.shape-circle/-diamond/-square/-triangle)/-card[data-tone]/-card-
     heading/-eyebrow(.eyebrow-push/-alert/-star/-ancla/-combo)/-stats/
     -line/-callout/-flag/-bands/-band/-actions/-bench(-label/-chip)),
     .promo-table-details, .promo-behavior-table (nowrap), .promo-quad
     (.q-star/-puzzle/-volume/-drain/-uncosted), .promo-exclude-btn,
     .promo-recomputing, .promo-recs-head, .promo-section-kicker,
     .promo-rec-sections, .promo-rec-group-head, .promo-recs-grid,
     .promo-rec-card(.push/.combo/.alert/.valley)/-card-top/h4/-metric/
     -action/-card-footer/-why, .promotion-name-cell, .promotion-products-
     cell, .promotion-margin-cell/-fc-cell/-sell-cell small, .promotion-
     actions-cell, .promo-pos/-neg/-warn, .promotion-type-field/-control/
     -hint, .promotion-field-label, .promotion-products-field/-product-
     search/-product-results/-result(-name/-meta)/-no-results, .promotion-
     selected-list/-item/-main/-qty/-remove/-empty-hint, .promotion-preview
     (-grid/-headline)
   Fuente v1: styles.css ~L14722-14998 (scope/dd/behavior/map primer trazo),
     ~L15000-15463 (mapa+recs, pasada "editorial"), ~L15464-15767 (mapa+recs,
     pasada "sobria" — ES la que gana por cascada: fusión last-wins de las
     3), ~L15768-15833 (celdas de tabla + modal), ~L9380-9600+~L8380-8790
     (app.js, markup real — 3 clases documentadas en styles.css resultaron
     SIN EMISOR y no se portan: .promo-map-corner, .promo-rec-icon,
     .promo-rec-pick, .promo-cat-label — verificado por grep en app.js/
     index.php, doctrina "la deuda muere aquí").
   Casos límite: sin categorías con datos → chips solo "Todas"; sin excluidos
     → .promo-scope-slot:empty se oculta (JS deja el div sin hijos: :empty no
     está en el reset, se resuelve aquí); mapa sin producto tocado → tarjeta
     "Explora tu carta" (sin acento,
     data-tone="neutral"/"neutro" cae al borde neutro por defecto, sin regla
     propia); sin sugerencias → párrafo .muted (JS); food cost incompleto →
     "sin costo"/"sin precio" en rojo (.promo-neg, ya cubre); tabla sin
     promos → fila vacía de .promotionsEmpty (feedback, fuera de aquí).
   Decisiones de porte:
     · --promo-green/-blue/-coral/-amber(-soft) (paleta ad-hoc de v1 para
       este módulo) se descartan: son un duplicado de --green/--blue/--red/
       --amber(-soft) que YA existen en 00-tokens.css. Mismo mapeo semántico
       de v1 (empujar=verde, revisar=rojo, estrella=ámbar, ancla=azul,
       combo=violeta) pero sin inventar una segunda familia de color.
     · .promo-rec-card.<tipo> fija --rec-accent/--rec-soft: badge.css ya deja
       .promo-rec-badge.badge-* con su propio --rec-accent de respaldo (uso
       suelto), pero el comentario de ese archivo es explícito: "el acento
       lo da la tarjeta" — .promo-rec-action y el borde superior de la
       tarjeta son descendientes de .promo-rec-card, no de .promo-rec-badge,
       así que el custom property tiene que nacer aquí para alcanzarlos.
     · .promo-rec-why > p pasa de flotar sobre la tarjeta (position:absolute
       + z-index + sombra) a expandirse inline: evita apilar otro nivel de
       z-index dentro de una grilla de tarjetas por un detalle secundario;
       la tarjeta simplemente crece.
     · Fondos con radial-gradient, blur y textura de puntos del escenario
       del mapa (pasada "editorial") no se portan: decoración, no geometría;
       la pasada "sobria" (que gana la fusión) ya los había retirado casi
       del todo.
     · .promotion-params (modal-grid + reset de margin) no necesita regla:
       .modal-grid en 04-componentes ya no trae margin propio que anular.
     · .promotion-type-field/-products-field (display:flex;flex-direction:
       column de v1) se omiten: sus hijos ya son bloques que se apilan solos;
       la regla no cambiaba nada.
     · pesos/tamaños fuera de escala → token más cercano (560→550, 620→650,
       640→650, 660→650, 12.5→12, 10.5→11, 640 idem); gaps de 1/5/7/9px → 2/
       6/6/8 (escala de espaciado); .promotion-remove pasa de 30×30 a
       32px×var(--control-h-compact) para alinearse con el input de
       cantidad de al lado, que ya usa ese token.
   NO cubre — gap fuera de esta vista: `.muted` (color secundario de texto)
     se usa en TODA la app pero no tiene regla propia en ningún archivo de
     css-v2 hoy (verificado 2026-08-09) — no se resuelve aquí por no ser
     exclusivo de Promociones.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Alcance del estudio: dos desplegables compactos en vez de un muro
     de chips (categorías a estudiar + productos excluidos) ─────────────── */
  .promo-scope-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 16px 0 4px; }
  .promo-scope-slot:empty { display: none; }   /* sin excluidos: la 2ª ranura no reserva hueco */

  .promo-dd { position: relative; }
  .promo-dd > summary {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 14px;
    border: 1px solid var(--line-2); border-radius: 999px;
    background: var(--surface); color: var(--ink-2);
    font-size: var(--fs-small); font-weight: 650;
    cursor: pointer; list-style: none; user-select: none;
  }
  .promo-dd > summary::-webkit-details-marker { display: none; }
  .promo-dd > summary::after { content: '▾'; color: var(--muted); }
  .promo-dd[open] > summary { border-color: var(--line-strong); }
  .promo-dd-panel {
    position: absolute; z-index: var(--z-dropdown); top: calc(100% + 8px); left: 0;
    display: flex; flex-wrap: wrap; gap: 6px;
    min-width: 280px; max-width: 440px; max-height: 320px; overflow-y: auto;
    padding: 12px;
    border: 1px solid var(--line-2); border-radius: var(--r-lg);
    background: var(--surface); box-shadow: var(--shadow-md);
  }

  /* ── Mapa de la carta: escenario (burbujas) + tarjeta de foco ──────────── */
  .promo-map { margin-top: 24px; padding: 24px; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); }
  .promo-map-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 16px; }
  .promo-recs-head .muted { display: block; max-width: 640px; font-size: var(--fs-small); line-height: 1.5; }
  .promo-recs { margin-top: 24px; }
  .promo-recs-empty { padding: 22px; border: 1px dashed var(--line-2); border-radius: var(--r); text-align: center; }
  .promo-map-lenses { display: inline-flex; gap: 4px; padding: 4px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface-2); }

  .promo-map-body { display: grid; grid-template-columns: minmax(0, 1fr) 300px; align-items: stretch; gap: 16px; }
  .promo-map-stage { position: relative; height: clamp(360px, 46vh, 520px); overflow: hidden; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface-2); }
  .promo-map-stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; touch-action: none; }

  /* Rótulos de cuadrante: mismo mapeo semántico que v1 (aporte × rotación) */
  .promo-map-zone {
    position: absolute; z-index: 1;
    display: flex; flex-direction: column; gap: 2px;
    max-width: 190px; padding: 8px 10px;
    border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface);
    pointer-events: none;
  }
  .promo-map-zone span { color: currentColor; font-size: var(--fs-caption); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; opacity: .8; }
  .promo-map-zone strong { color: var(--ink); font-size: var(--fs-small); line-height: 1.25; }
  .promo-map-zone.tl { top: 16px; left: 16px; color: var(--green); }
  .promo-map-zone.tr { top: 16px; right: 16px; color: var(--amber); text-align: right; }
  .promo-map-zone.bl { bottom: 40px; left: 16px; color: var(--red); }
  .promo-map-zone.br { right: 16px; bottom: 40px; color: var(--blue); text-align: right; }

  .promo-map-axis { position: absolute; z-index: 1; color: var(--muted-2); font-size: var(--fs-caption); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; pointer-events: none; }
  .promo-map-axis.axis-y { top: 50%; left: 8px; transform: translate(-42%, -50%) rotate(-90deg); }
  .promo-map-axis.axis-x { bottom: 12px; left: 50%; transform: translateX(-50%); }

  .promo-map-legend { position: absolute; z-index: 1; right: 16px; bottom: 12px; display: flex; align-items: center; gap: 12px; color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
  .promo-map-legend span { display: inline-flex; align-items: center; gap: 4px; }   /* incluye .promo-map-legend-note: hereda de aquí, sin regla propia */
  .promo-map-legend i { width: 7px; height: 7px; border-radius: 50%; }
  .promo-map-legend .is-opportunity { background: var(--green); }
  .promo-map-legend .is-star { background: var(--amber); }
  .promo-map-legend .is-alert { background: var(--red); }
  /* Con 2+ categorías el mapa distingue por FORMA (color queda para el
     comportamiento): la leyenda de puntos se reemplaza por una de formas,
     centrada arriba para no competir con las burbujas de las esquinas. */
  .promo-map-legend.has-categories {
    top: 12px; right: auto; bottom: auto; left: 50%; transform: translateX(-50%);
    max-width: calc(100% - 32px);
    padding: 4px 8px; border: 1px solid var(--line); border-radius: var(--r-sm);
    background: color-mix(in srgb, var(--surface) 90%, transparent);
  }
  .promo-map-legend-title { color: var(--muted); font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
  .promo-map-legend-note { white-space: nowrap; }
  .promo-map-legend-sep { align-self: stretch; width: 1px; background: var(--line); }
  .promo-map-legend .promo-legend-shape { width: 8px; height: 8px; flex: none; background: var(--ink); }
  .promo-map-legend .shape-circle { border-radius: 50%; }
  .promo-map-legend .shape-diamond { border-radius: 1px; transform: rotate(45deg) scale(.82); }
  .promo-map-legend .shape-square { border-radius: 1px; }
  .promo-map-legend .shape-triangle { width: 9px; height: 9px; clip-path: polygon(50% 0, 100% 88%, 0 88%); }

  /* Tarjeta de foco: crece con el escenario (grid con stretch); su acento
     de borde superior es el mismo semáforo que las zonas de arriba. */
  .promo-map-card { display: flex; flex-direction: column; gap: 12px; padding: 20px; border: 1px solid var(--line); border-top: 2px solid var(--line-2); border-radius: var(--r-lg); background: var(--surface); }
  .promo-map-card[data-tone="empujar"]  { border-top-color: var(--green); }
  .promo-map-card[data-tone="revisar"]  { border-top-color: var(--red); }
  .promo-map-card[data-tone="estrella"] { border-top-color: var(--amber); }
  .promo-map-card[data-tone="ancla"]    { border-top-color: var(--blue); }
  .promo-map-card[data-tone="combo"]    { border-top-color: var(--violet); }
  .promo-map-card-heading { display: grid; gap: 6px; }
  .promo-map-card h4 { margin: 0; font-size: var(--fs-h3); line-height: 1.2; }
  .promo-map-eyebrow { width: fit-content; color: var(--muted); font-size: var(--fs-caption); font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
  .promo-map-eyebrow.eyebrow-push  { color: var(--green); }
  .promo-map-eyebrow.eyebrow-alert { color: var(--red); }
  .promo-map-eyebrow.eyebrow-star  { color: var(--amber); }
  .promo-map-eyebrow.eyebrow-ancla { color: var(--blue); }
  .promo-map-eyebrow.eyebrow-combo { color: var(--violet); }

  .promo-map-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0; padding: 10px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
  .promo-map-stats > div { min-width: 0; padding: 0 8px; border-right: 1px solid var(--line); }
  .promo-map-stats > div:first-child { padding-left: 0; }
  .promo-map-stats > div:last-child { padding-right: 0; border-right: 0; }
  .promo-map-stats small, .promo-map-stats span { display: block; overflow: hidden; color: var(--muted); font-size: var(--fs-caption); text-overflow: ellipsis; white-space: nowrap; }
  .promo-map-stats strong { display: block; margin: 4px 0 2px; font-size: var(--fs-body-lg); }

  .promo-map-line { margin: 0; font-size: var(--fs-small); line-height: 1.5; }
  .promo-map-callout { padding-left: 10px; border-left: 2px solid var(--green); color: var(--ink-2); }
  .promo-map-flag { display: inline-block; padding: 2px 8px; border-radius: 999px; background: var(--violet-soft); color: var(--violet); font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; }

  .promo-map-bands { display: flex; align-items: flex-end; gap: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
  .promo-map-band { flex: 1; display: grid; justify-items: center; gap: 2px; color: var(--muted); font-size: var(--fs-caption); }
  .promo-map-band i { display: block; width: 28px; max-width: 100%; border-radius: var(--r-xs) var(--r-xs) 0 0; background: var(--blue); }
  .promo-map-band span { color: var(--ink-2); font-weight: 700; }

  .promo-map-actions { margin-top: auto; display: flex; flex-wrap: wrap; gap: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
  .promo-map-actions .primary-action { flex: 1 1 100%; justify-content: center; }
  .promo-map-actions .table-action { flex: 1; justify-content: center; }

  .promo-map-bench { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line); }
  .promo-map-bench-label { color: var(--muted); font-size: var(--fs-small); font-weight: 700; }
  .promo-map-bench-chip { padding: 4px 10px; border: 1px dashed var(--line-2); border-radius: 999px; color: var(--ink-2); font-size: var(--fs-small); }
  .promo-map-bench-chip small { color: var(--muted); }

  /* ── Comportamiento de productos: tabla oculta tras un disclosure ──────── */
  .promo-table-details { margin: 12px 0 4px; }
  .promo-table-details > summary { padding: 4px 0; color: var(--muted); font-size: var(--fs-small); font-weight: 650; cursor: pointer; user-select: none; }
  .promo-table-details[open] > summary { color: var(--ink); }
  .promo-behavior { margin-top: 12px; }
  .promo-behavior-table th, .promo-behavior-table td { white-space: nowrap; }
  .promo-quad { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-small); font-weight: 650; }
  .promo-quad::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
  .promo-quad.q-star { color: var(--green); }
  .promo-quad.q-puzzle { color: var(--amber); }
  .promo-quad.q-volume, .promo-quad.q-drain { color: var(--red); }
  .promo-quad.q-uncosted { color: var(--muted); }
  .promo-exclude-btn { padding: 0 4px; border: 0; background: transparent; color: var(--muted); font-size: var(--fs-body-lg); line-height: 1; opacity: .55; cursor: pointer; }
  .promo-exclude-btn:hover { color: var(--red); opacity: 1; }
  .promo-recomputing { color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }

  /* ── Recomendador: grupos (empuja/combos/alertas/valle) en grilla ──────── */
  .promo-section-kicker { display: block; margin-bottom: 4px; color: var(--muted); font-size: var(--fs-caption); font-weight: 800; letter-spacing: .1em; text-transform: uppercase; }
  .promo-rec-sections { display: grid; gap: 24px; }
  .promo-rec-group { min-width: 0; }
  .promo-rec-group-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
  .promo-rec-group-head h4 { margin: 0; font-size: var(--fs-body-lg); }
  .promo-rec-group-head p { margin: 0; color: var(--muted); font-size: var(--fs-caption); }
  .promo-rec-group-head > span { color: var(--muted); font-size: var(--fs-caption); font-weight: 700; }
  .promo-recs-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; }

  /* Tarjeta: --rec-accent/--rec-soft nacen aquí (por tipo) y bajan a
     .promo-rec-badge (badge.css) y a .promo-rec-action (abajo). */
  .promo-rec-card {
    display: flex; flex-direction: column; gap: 8px;
    min-height: 200px; padding: 16px;
    border: 1px solid var(--line); border-top: 2px solid var(--rec-accent, var(--line-2)); border-radius: var(--r);
    background: var(--surface); box-shadow: var(--shadow-xs);
    transition: transform var(--t), box-shadow var(--t), border-color var(--t);
  }
  .promo-rec-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); border-color: color-mix(in srgb, var(--rec-accent, var(--line-2)) 45%, var(--line)); }
  .promo-rec-card.push   { --rec-accent: var(--green);  --rec-soft: var(--green-soft); }
  .promo-rec-card.combo  { --rec-accent: var(--blue);   --rec-soft: var(--blue-soft); }
  .promo-rec-card.alert  { --rec-accent: var(--red);    --rec-soft: var(--red-soft); }
  .promo-rec-card.valley { --rec-accent: var(--amber);  --rec-soft: var(--amber-soft); }
  .promo-rec-card-top { display: flex; align-items: center; }
  .promo-rec-card h4 { margin: 4px 0 0; font-size: var(--fs-body-lg); line-height: 1.25; }
  .promo-rec-metric { margin: 0; color: var(--muted); font-size: var(--fs-small); line-height: 1.4; }
  .promo-rec-action { padding: 8px 10px; border: 1px solid color-mix(in srgb, var(--rec-accent, var(--line-2)) 16%, var(--line)); border-radius: var(--r-sm); background: color-mix(in srgb, var(--rec-soft, var(--surface-2)) 60%, var(--surface)); font-size: var(--fs-small); line-height: 1.4; }
  .promo-rec-action strong { color: var(--rec-accent, var(--ink)); }
  .promo-rec-card-footer { margin-top: auto; display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 8px; }
  .promo-rec-why { margin: 0; }
  /* Cerrado el <details> es solo su summary y convive con el botón en una
     fila. Abierto necesita el ancho entero: como ítem flex sin crecer se
     quedaba con lo que sobraba del botón (68px) y el párrafo salía en una
     columna de una palabra. Con basis 100% se lleva su propia fila y el
     botón baja — que es justo lo que dice el contrato de esta pasada: la
     explicación se expande inline y la tarjeta crece. */
  .promo-rec-why[open] { flex: 1 0 100%; }
  .promo-rec-why > summary { color: var(--muted); font-size: var(--fs-caption); }
  .promo-rec-why > p { margin: 6px 0 0; padding: 8px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface-2); color: var(--ink-2); font-size: var(--fs-caption); }

  /* ── Tabla de promociones creadas ───────────────────────────────────────
     11 columnas (nombre+tipo, productos, 5 cifras, vigencia, estado,
     acciones): sin ancho mínimo se aprietan antes de ofrecer scroll. */
  .promotions-table { min-width: 1360px; }
  .promotions-wrap { margin-top: 12px; }
  .promotions-section { min-width: 0; }
  .promotions-intro { max-width: 720px; }
  .promotion-name-cell { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
  .promotion-products-cell { max-width: 260px; color: var(--ink-2); }
  .promotion-margin-cell small, .promotion-fc-cell small, .promotion-sell-cell small { display: block; color: var(--muted); font-size: var(--fs-caption); font-weight: 550; }
  .promotion-actions-cell { display: flex; justify-content: flex-end; gap: 6px; white-space: nowrap; }

  /* Semáforo compartido por tabla, mapa y modal (positivo/negativo/aviso) */
  .promo-pos { color: var(--green); }
  .promo-neg { color: var(--red); }
  .promo-warn { color: var(--amber); }

  /* ── Modal: formulario de simulación ─────────────────────────────────── */
  .promotion-field-label { display: block; margin-bottom: 6px; color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
  .promotion-type-field,
  .promotion-products-field { display: flex; flex-direction: column; }
  .promotion-params { margin: 0; }
  .promotion-type-control { width: 100%; }
  .promotion-type-control button { flex: 1; }
  .promotion-type-hint { margin: 8px 0 0; font-size: var(--fs-caption); }

  .promotion-product-search { position: relative; }
  .promotion-product-results {
    position: absolute; z-index: var(--z-dropdown); top: calc(100% + 4px); left: 0; right: 0;
    max-height: 260px; overflow-y: auto; padding: 4px;
    border: 1px solid var(--line-2); border-radius: var(--r); background: var(--surface); box-shadow: var(--shadow-md);
  }
  .promotion-result { display: flex; flex-direction: column; gap: 2px; width: 100%; padding: 6px 8px; border: 0; border-radius: var(--r-sm); background: transparent; text-align: left; cursor: pointer; }
  .promotion-result:hover { background: var(--surface-2); }
  .promotion-result-name { color: var(--ink); font-size: var(--fs-small); font-weight: 650; }
  .promotion-result-meta { color: var(--muted); font-size: var(--fs-caption); }
  .promotion-no-results { padding: 8px; }

  .promotion-selected-list { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
  .promotion-empty-hint { margin-top: 4px; }
  .promotion-selected-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .promotion-selected-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .promotion-selected-main strong { font-size: var(--fs-small); font-weight: 650; }
  .promotion-selected-main small { color: var(--muted); font-size: var(--fs-caption); }
  .promotion-qty { display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: var(--fs-caption); }
  .promotion-qty input { width: 64px; height: var(--control-h-compact); }
  /* icon-button (boton.css) a 40px es demasiado alto junto a un input de
     32px: se empareja con var(--control-h-compact), no un número suelto. */
  .promotion-remove { width: 32px; height: var(--control-h-compact); flex: none; }

  .promotion-preview { padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface-2); }
  .promotion-preview-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px 14px; }
  .promotion-preview-grid > div { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .promotion-preview-grid small { color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
  .promotion-preview-grid strong { font-size: var(--fs-body-lg); font-weight: 800; }
  .promotion-preview-headline { margin-top: 12px; font-size: var(--fs-small); font-weight: 650; }

  /* ── Responsive (v1: 1320/980 ad-hoc → 1100/900 canon; .promo-recs-grid
     no necesita quiebre propio, auto-fit ya la reduce a 1 columna) ──────── */
  @media (max-width: 900px) {
    .promo-map-body { grid-template-columns: 1fr; }
  }

  @media (max-width: 680px) {
    .promo-map { padding: 16px; }
    .promo-map-stage { height: 340px; }
    .promo-map-legend { display: none; }
  }
}

/* líneas: ~311 de presupuesto 150 — excedido y documentado (como eventos.css
   en P3): la vista concentra 5 sub-sistemas propios sin dueño en 04-
   componentes (dropdowns de alcance, mapa de la carta completo con
   escenario+leyenda+tarjeta, recomendador con 4 acentos semánticos, celdas
   de la tabla de promos, interior completo del formulario modal) — ninguno
   se reutiliza fuera de Promociones (verificado por grep en styles.css/
   app.js), así que no corresponde subirlo a un componente compartido. */
/* ══ tableros (data-view="reports") ══════════════════════════════════════
   Cubre: #view-reports .report-dashboard-section, #view-reports
     .report-dashboard, #view-reports .report-chart-grid, #view-reports
     .report-rank-table, #view-reports #reportKpiStrip.
   Fuente v1: styles.css ~L6532-6542 (.report-dashboard-section, fusionada
     con su reafirmación idéntica en el breakpoint 1180 ~L10469 — sin
     cambios reales entre anchos), ~L6586-6597 (.report-dashboard: UNA sola
     columna en toda la cascada, incl. el breakpoint 1180 que repite el
     mismo valor — no existe versión a 2 columnas pese a que el panel
     lateral "parece" candidato a ir junto al gráfico: se verificó en vivo,
     ver Casos límite), ~L6740-6745 (.report-chart-grid), ~L6766
     (.report-rank-table), ~L13658-13666 (#reportKpiStrip: hay una regla
     `!important` que la apaga a favor de `.ph-stats` del page-header —
     ver Casos límite).
   Casos límite: gráfico sin datos → `.empty` que pinta app.js dentro de
     `.report-chart` (ya vestido en 02-base, sin regla aquí); ranking sin
     filas o Tabulator caído → `.report-fallback-row` (panel.css, esta
     vista no la toca); período con un solo mes → el gráfico igual reserva
     su alto mínimo (panel.css: report-main-chart 560/430).
   Nota — #reportKpiStrip verificado EN VIVO (v1, localhost:8080/?css=v1,
     2026-08-09): el elemento existe con datos pero se renderiza con
     `display:none` — v1 lo apaga a propósito porque sus mismos indicadores
     ya se muestran en `.ph-stats` del page-header (comentario propio de
     v1: "These views now show their KPIs in the header → hide the
     redundant in-view strips"). Esa regla `!important` NO se portó a
     ningún 04-componentes (kpi.css trata `.report-kpi-strip` como viva en
     su receta de tarjeta); se resuelve aquí, sin !important — el layer
     `vistas` ya gana por orden de cascada a `componentes`. Afecta a 7
     vistas hermanas más (Libro de compras, Ventas, Ing. menú, Conciliación,
     MoM, Consumo, Inventario) que tropezarán con el mismo hueco al
     construirse — ver NOTAS-P2.md.
   Nota — .report-dashboard-section/.reports-section/.report-board-head/
     .reports-head se repiten IDÉNTICAS en #view-menu (Ingeniería de menú,
     verificado grep 2026-08-09): esta vista solo despoja la caja de
     `.table-section` (borde/fondo/padding) dentro de `#view-reports`, sin
     tocar la clase en general — Ingeniería de menú deberá repetir la misma
     regla bajo `#view-menu` (o, mejor, promoverla a variante nombrada en
     panel.css) — ver NOTAS-P2.md.
   NO cubre — `.report-detail-modal`/`.report-detail-table` (modal de
     detalle, fuera de `#view-reports`: vive como `.modal-backdrop` hermano
     al final del DOM). El primero ya está en modal.css; el segundo
     (`height: 70vh`, mismo truco de alto que `.report-rank-table` de aquí)
     falta sumarlo ahí — no es geometría de esta vista.
   ═══════════════════════════════════════════════════════════════════════ */

@layer vistas {

  .report-chart-scope { display: grid; grid-template-columns: 1fr; min-width: 0; }
  .report-chart-scope-inner { display: flex; align-items: center; gap: 2px; min-width: 0; overflow: hidden; }

  /* El gráfico principal reúne hasta seis opciones en el mismo segmentado;
     con el texto explicativo al lado se comprimía el bloque de alcance y
     dejaba “Solo restaurante/eventos” cortado. La cabecera usa dos filas. */
  #view-reports .report-main-panel .report-panel-head {
    flex-direction: column;
    align-items: stretch;
  }
  #view-reports .report-main-panel .report-panel-actions {
    width: 100%;
    justify-content: flex-start;
  }

  /* .table-section aporta solo la clase semántica: sus 2 hijos reales
     (.report-board-head y .report-dashboard) ya traen su propia caja
     (panel.css/toolbar.css) — envolverlos otra vez duplicaría el borde. */
  #view-reports .report-dashboard-section {
    padding: 0;
    margin-top: 0;
    border: 0;
    background: transparent;
    display: grid;
    gap: 8px;
  }

  /* Apagada a favor de .ph-stats del page-header (ver Nota arriba). */
  #view-reports #reportKpiStrip { display: none; }

  /* Tablero: gráfico grande arriba, ranking completo abajo — una sola
     columna en toda la cascada de v1 (no hay variante a 2 columnas). */
  #view-reports .report-dashboard {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
  }

  /* Área de gráfico del panel principal: hoy un solo gráfico; el grid
     deja espacio si el tablero suma un segundo cuadrante. */
  #view-reports .report-chart-grid {
    min-height: 0;
    display: grid;
    gap: 8px;
  }

  /* El ranking es un Tabulator con height:'100%' (JS) — necesita que su
     contenedor resuelva un alto definido dentro de la fila 1fr de
     .report-panel; mismo truco que .report-chart en panel.css. */
  #view-reports .report-rank-table { height: 100%; }
}

/* líneas: ~85 de presupuesto 150 (cabecera larga: 3 hallazgos de porte
   documentados en vivo, no solo geometría — ver notas arriba) */
/* ══ taller (vista) ═══════════════════════════════════════════════════════
   Vista: "Taller" · #view-mente / data-view="mente" (permiso "mente";
     nombre visible renombrado a "Taller" 2026-08-06 — ver mente-module.md).
   EXCEPCIÓN A LA REGLA DE VISTAS (igual que Personas): esta isla completa
     — canvas de esferas + drawer de proyecto + tarjeta hover + panel del
     aro — no compone sobre 04-componentes, es un widget bespoke bajo
     prefijo .mente-* propio. Este archivo reescribe mente.css ENTERO
     (no solo su geometría) sobre tokens v2.
   Cubre: .mente-root/-stage(+canvas), -top(-ttl/-sub/-acts), -btn(+-primary
     /.on), -egg(.on), -card(+-row/-area/-dot), -lbl, -bar, -urg, -legend,
     -emptyhint, -drawer(.open), -dclose, -dhead(-dtop/-dwho/-darearow/-ddot),
     #menteDArea(+option), #menteDName, -dring(+#menteRingTxt), -stepcount,
     -dctrls, -ctrl(+input[range]/[date]), -val, -dbody, -sec(.mente-danger-sec),
     -cnt, -empty, -stepwrap, -step-head(.done/.open), -schk, -badges, -bdg,
     -caret, -del, -step-exp, -addrow, -drop(.drag), -file, -fic, -thumb,
     -fmeta, -fn, -fs, -cm, -av, -cbody, -cmeta, -ctxt, -cmbox, -expsub,
     #menteTags/.mente-tag, #menteTagInput, #menteDelProj, -hooppanel(.show),
     -hphead, #menteHoopReset, -hctrl(+input[range]), -hpin, -hphint.
   Fuente v1: mente.css completo (242 líneas) + styles.css ~L18384-18406
     (.mente-hpin y su variante compacta de .ui-switch: vivía fuera de
     mente.css por descuido de v1; migra aquí porque es geometría de Taller).
   NO cubre (fuera de alcance, documentado):
     · Colores de esferas/halos/anillos del canvas: los pinta mente.js
       vía JS inline/canvas, no hay CSS que tocar.
     · `.ui-switch` base (interruptor iOS crudo): también se usa en
       reordenar-sidebar y permisos por app.js — no es de Taller. Sin
       dueño hoy en 04-componentes/campo.css (mismo tipo de hueco que
       NOTAS-P2 #11); aquí solo se ubica su variante compacta dentro de
       `.mente-hpin`, que sí es geometría propia de este panel.
     · `input[type="range"]` no tiene receta en 02-base.css — el selector
       genérico `input{…}` (borde, alto 40px, fondo) cae también sobre los
       sliders porque no los excluye. Se neutraliza LOCAL bajo
       `.mente-ctrl`/`.mente-hctrl` para que Importancia/Urgencia/Rotación/
       Profundidad/Tamaño no se vean como campos de texto; el hueco de
       fondo (afecta a cualquier otro slider futuro) queda para 02-base.css.
   Cambios visibles (deuda que muere al portar):
     · Sin token naranja: el acento del easter egg/aro (#ff8a5c/#ff8a45)
       pasa a var(--amber-bright).
     · Foco de inputs propios (nombre, agregar paso, tag, comentario) deja
       el anillo índigo bespoke y hereda el foco canon de 02-base.css —
       mismo tratamiento que el resto de la app, no hacía falta uno propio.
     · Botón "Comentar": el texto usaba var(--green-deep), token que nunca
       existió (el fallback #2e7a4c ganaba siempre); pasa a var(--surface)
       sobre fondo verde, misma receta que .mente-btn-primary.
     · Bordes/sombras neutros que en v1 eran slate/índigo/negro crudos
       (rgba 148,163,184 · 79,70,229 · 15,23,42) migran a var(--line*) y a
       color-mix(in srgb, var(--ink) N%, transparent).
     · Radios y tipografía fuera de escala (9/11.5/13.5/18px, pesos
       500/600) redondeados al peldaño más próximo de las familias --r-* y
       --fs-*, y de los pesos permitidos.
     · Duraciones sueltas (.18s/.34s) pasan a var(--t)/var(--t-lg).
     · z-index locales (3/4/5/6) se reemplazan: barra superior/leyenda/
       vacío usan el modismo `z-index: 1` (único hermano de abajo: el
       canvas); tarjeta hover y panel del aro usan var(--z-popover); el
       drawer usa var(--z-modal). `.mente-stage` gana `isolation: isolate`
       para que esos tramos globales no compitan con el resto de la app.
     · Cruft de v1 retirado por redundante: `.mente-stage *`/`ul` (ya cubre
       01-reset.css), scrollbar propia de `.mente-dbody` (ya cubre la fina
       global), `.mente-sec:first-child{border-top}` (duplicaba el borde
       completo que `.mente-sec` ya trae), `backdrop-filter` del drawer
       (fondo opaco, sin efecto visible), `border-radius:0` de `.mente-step-head`
       (el padre ya recorta con `overflow:hidden`).
   Casos límite: sin proyectos → `.mente-emptyhint`; paso sin adjuntos ni
     comentarios → listas vacías, sin mensaje propio (ya así en v1); nombre
     de archivo/proyecto largo → ellipsis en `.mente-fn`, wrap normal en
     `#menteDName`.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  .mente-root { width: 100%; }

  .mente-stage {
    position: relative; isolation: isolate; width: 100%;
    height: clamp(520px, calc(100dvh - var(--nav-alto) - 152px), 1080px);
    overflow: hidden;
    border: 1px solid var(--line); border-radius: var(--r-lg);
    background: var(--surface-2);
    box-shadow: 0 24px 60px -34px color-mix(in srgb, var(--ink) 30%, transparent);
    user-select: none;
  }
  .mente-stage canvas { position: absolute; inset: 0; touch-action: none; }

  /* ── Barra superior: título + acciones. z-index:1 = modismo (único
     hermano de abajo es el canvas). La franja deja pasar el arrastre de
     esferas; solo .mente-acts reabre el puntero. ── */
  .mente-top {
    position: absolute; top: 0; left: 0; right: 0; z-index: 1;
    display: flex; align-items: flex-start; justify-content: space-between;
    padding: 16px; pointer-events: none;
  }
  .mente-ttl { color: var(--ink); font-size: var(--fs-body-lg); font-weight: 650; letter-spacing: .04em; }
  .mente-sub { margin-top: 2px; color: var(--muted); font-size: var(--fs-small); }
  .mente-acts { display: flex; align-items: center; gap: 8px; pointer-events: auto; }

  .mente-btn {
    padding: 8px 12px; border: 1px solid var(--line-2); border-radius: var(--r);
    background: color-mix(in srgb, var(--surface) 90%, transparent);
    box-shadow: 0 8px 22px -18px color-mix(in srgb, var(--ink) 50%, transparent);
    color: var(--ink-2); font-size: var(--fs-small); line-height: 1;
    transition: background var(--t), border-color var(--t), color var(--t);
  }
  .mente-btn:hover { background: var(--surface-2); border-color: var(--line-strong); }
  .mente-btn.on {
    border-color: color-mix(in srgb, var(--green) 42%, transparent);
    background: color-mix(in srgb, var(--green) 13%, transparent);
    color: var(--green);
  }
  .mente-btn-primary { border-color: var(--ink-2); background: var(--ink-2); color: var(--surface); }
  .mente-btn-primary:hover { border-color: var(--ink); background: var(--ink); }

  .mente-egg {
    padding: 6px; border-radius: var(--r-sm);
    font-size: var(--fs-body-lg); line-height: 1;
    opacity: .22; filter: grayscale(.4);
    transition: opacity var(--t), background var(--t);
  }
  .mente-egg:hover { opacity: .6; }
  .mente-egg.on { opacity: 1; filter: none; background: color-mix(in srgb, var(--amber-bright) 16%, transparent); }

  /* ── Tarjeta flotante al pasar sobre una esfera (drawer cerrado) ──── */
  .mente-card {
    position: absolute; z-index: var(--z-popover);
    display: none; min-width: 188px; max-width: 240px;
    padding: 12px 14px; border: 1px solid var(--line); border-radius: var(--r-lg);
    background: color-mix(in srgb, var(--surface) 94%, transparent);
    backdrop-filter: blur(10px);
    box-shadow: 0 16px 38px -24px color-mix(in srgb, var(--ink) 45%, transparent);
    color: var(--ink); pointer-events: none;
  }
  .mente-card-row { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
  .mente-card-area { justify-content: flex-start; }
  .mente-card-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; }
  #menteCardArea { color: var(--muted); font-size: var(--fs-caption); }
  #menteCardName { margin: 8px 0 12px; font-size: var(--fs-body-lg); font-weight: 550; line-height: 1.3; }
  .mente-lbl { color: var(--muted); font-size: var(--fs-caption); }
  #menteCardProg { font-size: var(--fs-small); font-weight: 550; }
  .mente-bar { height: 5px; margin: 4px 0 12px; border-radius: var(--r-xs); background: var(--surface-3); overflow: hidden; }
  #menteCardBar { height: 100%; width: 0%; border-radius: inherit; }
  .mente-urg { display: flex; gap: 2px; }

  /* Leyenda y aviso vacío: misma franja de chrome que .mente-top. */
  .mente-legend {
    position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
    display: flex; flex-wrap: wrap; gap: 16px; padding: 14px 20px;
    color: var(--muted); font-size: var(--fs-caption); pointer-events: none;
  }
  .mente-legend b { color: var(--ink); font-weight: 650; }

  .mente-emptyhint {
    position: absolute; top: 50%; left: 50%; z-index: 1;
    transform: translate(-50%, -50%);
    color: var(--muted); font-size: var(--fs-body-lg); text-align: center;
    pointer-events: none;
  }
  .mente-emptyhint strong { color: var(--ink-2); font-weight: 650; }

  /* ── Drawer de proyecto. var(--z-modal): mismo tramo que cualquier
     superposición modal — siempre gana a -card/-hooppanel (var(--z-popover)). */
  .mente-drawer {
    position: absolute; top: 0; right: 0; z-index: var(--z-modal);
    width: min(560px, 96%); height: 100%;
    display: flex; flex-direction: column;
    border-left: 1px solid var(--line-strong);
    background: var(--surface); color: var(--ink);
    box-shadow: -28px 0 64px -44px color-mix(in srgb, var(--ink) 62%, transparent);
    transform: translateX(100%);
    transition: transform var(--t-lg);
  }
  .mente-drawer.open { transform: translateX(0); }
  .mente-dclose {
    position: absolute; top: 14px; right: 14px;
    width: 30px; height: 30px; border-radius: var(--r-sm);
    display: grid; place-items: center;
    border: 1px solid var(--line); background: var(--surface-2); color: var(--muted);
    font-size: var(--fs-h3); line-height: 1;
  }
  .mente-dclose:hover { background: var(--surface-3); }

  .mente-dhead {
    padding: 24px 28px 16px;
    border-bottom: 1px solid var(--line-2);
    background: linear-gradient(180deg, var(--surface), var(--surface-2));
  }
  .mente-dtop { display: flex; align-items: flex-start; gap: 16px; }
  .mente-dwho { flex: 1; min-width: 0; padding-right: 24px; }
  .mente-darearow { display: flex; align-items: center; gap: 6px; }
  .mente-ddot { flex: none; width: 9px; height: 9px; border-radius: 50%; }

  /* Pisa la receta base de select (02-base): acá es texto plano junto al
     dot de área, con flecha nativa del SO — no un campo de formulario. */
  #menteDArea {
    width: auto; height: auto; padding: 0;
    appearance: auto; -webkit-appearance: auto;
    border: none; background: transparent; box-shadow: none;
    color: var(--muted); font-size: var(--fs-small); cursor: pointer;
  }
  #menteDArea option { color: var(--ink); }

  /* Pisa la receta base de input: título grande sin caja, con subrayado
     que solo aparece en foco. */
  #menteDName {
    height: auto; padding: 6px 0 4px; border: none; border-bottom: 1px solid transparent;
    border-radius: 0; background: transparent; box-shadow: none;
    font-size: var(--fs-h2); font-weight: 650;
  }
  #menteDName:focus { outline: none; box-shadow: none; border-bottom-color: var(--accent); }

  .mente-dring { position: relative; flex: none; width: 56px; height: 56px; }
  .mente-dring svg circle:first-child { stroke: var(--line); }
  #menteRingTxt {
    position: absolute; inset: 0; display: grid; place-items: center;
    color: var(--ink); font-size: var(--fs-small); font-weight: 700;
  }
  .mente-stepcount { margin-top: 10px; color: var(--muted); font-size: var(--fs-small); }

  .mente-dctrls {
    display: flex; flex-direction: column; gap: 10px; margin-top: 16px; padding: 14px;
    border: 1px solid var(--line-2); border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: 0 12px 34px -28px color-mix(in srgb, var(--ink) 40%, transparent);
  }
  .mente-ctrl { display: flex; align-items: center; gap: 10px; }

  /* Pisa la receta base de label (02-base): fila horizontal compacta con
     etiqueta fija a la izquierda, no el bloque de mayúsculas apilado del
     canon (que asume "label arriba, campo abajo"). */
  .mente-ctrl label, .mente-hctrl label {
    display: block; width: 104px; flex: none;
    color: var(--muted); font-size: var(--fs-small); font-weight: 650;
    text-transform: none; letter-spacing: normal;
  }
  .mente-hctrl label { width: 72px; font-size: var(--fs-caption); }

  /* Sliders: neutraliza la caja de input genérica de 02-base (sin receta
     propia para type="range" — ver cabecera). Track/thumb nativos del
     navegador, coloreados solo con accent-color. */
  .mente-ctrl input[type="range"], .mente-hctrl input[type="range"] {
    flex: 1; min-width: 0; height: 4px; padding: 0;
    border: none; background: none; box-shadow: none;
  }
  #menteImpR { accent-color: var(--cyan); }
  #menteUrgR { accent-color: var(--amber-bright); }
  .mente-hctrl input[type="range"] { accent-color: var(--amber-bright); }

  .mente-val { width: 34px; color: var(--ink); font-size: var(--fs-small); font-weight: 650; text-align: right; }
  .mente-ctrl input[type="date"] { flex: 1; min-width: 0; font-size: var(--fs-small); color-scheme: light; }

  .mente-dbody { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 28px 32px; background: var(--surface-2); }

  .mente-sec {
    margin-bottom: 16px; padding: 16px;
    border: 1px solid var(--line-2); border-radius: var(--r-lg);
    background: var(--surface);
    box-shadow: 0 12px 34px -30px color-mix(in srgb, var(--ink) 46%, transparent);
  }
  .mente-danger-sec { border-color: color-mix(in srgb, var(--red) 38%, transparent); background: var(--red-soft); }
  .mente-danger-sec h3 { color: var(--red); }
  .mente-sec h3 {
    display: flex; align-items: center; justify-content: space-between;
    margin: 0 0 12px; color: var(--muted);
    font-size: var(--fs-caption); font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  }
  .mente-cnt { color: var(--muted-2); font-weight: 650; letter-spacing: normal; text-transform: none; }
  .mente-empty { padding: 4px 0; color: var(--muted-2); font-size: var(--fs-body); }

  .mente-stepwrap {
    margin-bottom: 8px; overflow: hidden;
    border: 1px solid var(--line-2); border-radius: var(--r-lg);
    background: var(--surface-2);
  }
  .mente-step-head { display: flex; align-items: center; gap: 10px; padding: 12px; cursor: pointer; }
  .mente-step-head:hover { background: var(--surface); }
  .mente-schk { flex: none; width: 17px; height: 17px; accent-color: var(--green-vivo); cursor: pointer; }
  .mente-step-head .mente-txt { flex: 1; color: var(--ink); font-size: var(--fs-body); line-height: 1.4; }
  .mente-step-head.done .mente-txt { color: var(--muted-2); text-decoration: line-through; }
  .mente-badges { display: flex; flex: none; gap: 8px; }
  .mente-bdg { display: inline-flex; align-items: center; gap: 2px; color: var(--muted); font-size: var(--fs-caption); }
  .mente-caret { flex: none; display: flex; color: var(--muted-2); transition: transform var(--t); }
  .mente-step-head.open .mente-caret { transform: rotate(90deg); }
  .mente-del { flex: none; padding: 2px; border-radius: var(--r-xs); color: var(--muted-2); }
  .mente-del:hover { background: color-mix(in srgb, var(--red) 12%, transparent); color: var(--red); }
  .mente-step-exp { padding: 12px 14px 14px 40px; border-top: 1px solid var(--line-2); background: var(--surface); }

  .mente-addrow { display: flex; gap: 8px; margin-top: 12px; }
  .mente-addrow input { flex: 1; min-width: 0; }
  .mente-addrow button {
    flex: none; width: var(--control-h); height: var(--control-h);
    display: grid; place-items: center;
    border-radius: var(--r-sm); background: var(--ink-2); color: var(--surface);
    font-size: var(--fs-h3); line-height: 1;
  }
  .mente-addrow button:hover { background: var(--ink); }

  .mente-drop {
    padding: 12px; border: 1px dashed var(--line-strong); border-radius: var(--r);
    background: var(--surface-2);
    color: var(--muted); font-size: var(--fs-caption); text-align: center;
    transition: background var(--t), border-color var(--t), color var(--t);
  }
  .mente-drop:hover { border-color: var(--accent); background: var(--surface); color: var(--ink-2); }
  .mente-drop.drag { border-color: var(--green); background: color-mix(in srgb, var(--green) 8%, transparent); color: var(--green); }

  .mente-file { display: flex; align-items: center; gap: 10px; padding: 6px 0; }
  .mente-fic, .mente-thumb { flex: none; width: 32px; height: 32px; border-radius: var(--r-sm); }
  .mente-fic { display: grid; place-items: center; background: var(--surface-3); color: var(--muted); }
  .mente-thumb { object-fit: cover; }
  .mente-fmeta { flex: 1; min-width: 0; display: flex; flex-direction: column; }
  .mente-fn { overflow: hidden; color: var(--ink); font-size: var(--fs-small); text-overflow: ellipsis; white-space: nowrap; }
  .mente-fn a { color: inherit; text-decoration: none; }
  .mente-fn a:hover { text-decoration: underline; }
  .mente-fs { color: var(--muted-2); font-size: var(--fs-caption); }

  .mente-cm { display: flex; gap: 10px; padding: 8px 0; }
  .mente-av {
    flex: none; width: 28px; height: 28px; border-radius: 50%;
    display: grid; place-items: center;
    color: var(--surface); font-size: var(--fs-caption); font-weight: 650;
  }
  .mente-cbody { flex: 1; min-width: 0; }
  .mente-cmeta { display: block; margin-bottom: 2px; color: var(--muted); font-size: var(--fs-caption); }
  .mente-ctxt { display: block; font-size: var(--fs-body); line-height: 1.45; }

  .mente-cmbox { margin-top: 8px; }
  .mente-cmbox textarea { min-height: 48px; border-radius: var(--r); }
  .mente-cmbox button {
    margin-top: 6px; padding: 6px 14px;
    border-radius: var(--r-sm); background: var(--green); color: var(--surface);
    font-size: var(--fs-small); font-weight: 700;
  }
  .mente-cmbox button:hover { background: var(--green-vivo); }
  .mente-expsub { margin: 14px 0 6px; color: var(--muted); font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }

  #menteTags { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
  .mente-tag {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 4px 6px 4px 10px; border: 1px solid var(--line-2); border-radius: 999px;
    background: var(--surface-3); color: var(--ink-2); font-size: var(--fs-small);
  }
  .mente-tag button { padding: 0 2px; color: var(--muted-2); font-size: var(--fs-body-lg); line-height: 1; }
  .mente-tag button:hover { color: var(--red); }
  #menteTagInput { width: 132px; height: var(--control-h-compact); padding: 0 10px; border-radius: 999px; border-color: var(--line-strong); }

  #menteDelProj {
    width: 100%; margin-top: 2px; padding: 10px;
    border: 1px solid color-mix(in srgb, var(--red) 30%, transparent); border-radius: var(--r);
    background: var(--red-soft); color: var(--red);
    font-size: var(--fs-body); font-weight: 700;
  }
  #menteDelProj:hover { background: color-mix(in srgb, var(--red) 14%, var(--surface)); }

  /* ── Panel del aro (easter egg): flota sobre el canvas, mismo tramo que
     la tarjeta hover. ── */
  .mente-hooppanel {
    position: absolute; left: 16px; bottom: 48px; z-index: var(--z-popover);
    display: none; width: 242px; padding: 12px 14px;
    border: 1px solid var(--line); border-radius: var(--r-lg);
    background: color-mix(in srgb, var(--surface) 94%, transparent);
    backdrop-filter: blur(10px);
    box-shadow: 0 16px 38px -26px color-mix(in srgb, var(--ink) 45%, transparent);
    color: var(--ink);
  }
  .mente-hooppanel.show { display: block; }
  .mente-hphead { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; font-size: var(--fs-small); font-weight: 650; }
  #menteHoopReset {
    padding: 2px 8px; border: 1px solid var(--line-2); border-radius: var(--r-sm);
    background: var(--surface-2); color: var(--muted); font-size: var(--fs-caption);
  }
  #menteHoopReset:hover { background: var(--surface-3); }
  .mente-hctrl { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
  .mente-hphint { margin-top: 6px; color: var(--muted); font-size: var(--fs-caption); }

  /* Fila "Fijar posición": label + switch repartidos a los extremos, sin
     slider — el ancho fijo de .mente-hctrl label no aplica acá. */
  .mente-hpin { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
  .mente-hpin label { width: auto; }
  .mente-hpin .ui-switch input { width: 36px; height: 22px; }
  .mente-hpin .ui-switch input::before { width: 16px; height: 16px; }
  .mente-hpin .ui-switch input:checked::before { transform: translateX(14px); }

  /* ── Responsive: v1 quebraba en 720px (ad-hoc) → consolidado a 680. ── */
  @media (max-width: 680px) {
    .mente-top { flex-direction: column; gap: 10px; }
    .mente-acts { flex-wrap: wrap; }
    .mente-stage { height: clamp(460px, calc(100dvh - var(--nav-alto) - 124px), 900px); }
    .mente-drawer { width: 100%; }
    .mente-dhead { padding: 16px 20px; }
    .mente-dbody { padding: 16px 16px 28px; }
    .mente-ctrl { flex-direction: column; align-items: flex-start; gap: 6px; }
    .mente-ctrl label { width: auto; }
  }
}

/* líneas: ~330 (excede presupuesto de vista 150: isla completa, ver
   cabecera — mismo criterio que Personas) */
/* ══ tareas-diarias (#view-daily) ═══════════════════════════════════════════
   Cubre: .daily-work-section, .daily-work-toolbar(fino), .daily-date-tools
     .compact-date(fino), .daily-task-quick-panel, .daily-task-quick-form
     (+ .daily-task-title-field/-detail-field/-form-actions), .daily-board,
     .daily-column(+-head/-head span), .daily-task-list, .daily-event-context,
     .daily-event-panel(+-head), .daily-event-list/-item, .daily-empty,
     .daily-task-card(+--doing/--blocked/--done/.is-expanded),
     .daily-task-summary(+-side/-chevron), .daily-task-body(+-inner),
     .daily-priority(+--urgent/--high/--low), .daily-task-description,
     .daily-linked-event, .daily-task-meta, .daily-task-actions,
     .daily-thread(+-list/-empty), .daily-comment(+-form), .daily-attachments
     (+-list), .daily-attachment(+-form)
   Fuente v1: styles.css ~L8358-8956 (geometría + identidad completas de la
     vista) fusionada con los breakpoints dispersos ~L8489-8588, ~L10217-10251,
     ~L10578-10880 (4→2→1 columnas, dos cascadas contradictorias en tablero y
     formulario — ver Cambios visibles). .daily-date-tools/.daily-work-toolbar
     ya reciben su ritmo (gap, align-items, captions) de 04-componentes/
     toolbar.css: aquí solo el ancho fino del input de fecha.
   Casos límite: columna sin tareas → .daily-empty; día sin eventos → panel
     de agenda corto sin lista; comentario/adjunto/título largo → wrap
     (overflow-wrap: anywhere, nunca ellipsis en el detalle de una tarjeta
     expandida); tarjeta colapsada no reserva alto (grid-template-rows: 0fr).
   Nota de propiedad: ninguna clase .daily-* aparece en otra vista (verificado
     en contrato-dom.json) y no están en la lista de 17 componentes núcleo
     (el único candidato, .daily-summary, es la tira KPI MUERTA que
     04-componentes/kpi.css ya documenta y no porta). Como esta vista es la
     única dueña de la tarjeta/columna/prioridad, además de acomodarlas fija
     su identidad mínima de color — siempre con tokens semánticos, nunca
     crudo — en vez de dejarlas sin estilo.
   Excluidas a propósito: .daily-summary (KPI muerta), .daily-work-grid y
     .daily-task-form-grid (sin emisor en el DOM actual — el grid del form
     vive directo en .daily-task-quick-form).
   Cambios visibles: tarjeta sin sombra de reposo (canon panel: "sin sombra
     permanente", solo la usan overlays) — v1 traía 0 8px 20px permanente;
     acciones del formulario pasan de un roto "grid-column: 8" (grid de 4
     columnas, bug heredado) a "1 / -1" ocupando la fila completa.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Envoltorio: el panel de .table-section se apaga — los paneles reales
     son el toolbar, el formulario y cada columna del tablero. ── */
  .daily-work-section { margin-top: 0; padding: 0; border: 0; background: transparent; }

  /* ── Barra de fecha: el ritmo (gap, align-items) ya lo da toolbar.css;
     aquí solo el ancho, para que el input no se estire a toda la fila. ── */
  .daily-work-toolbar { margin-bottom: 10px; }
  .daily-date-tools .compact-date { width: auto; min-width: 148px; max-width: 190px; flex: none; }

  /* ── Formulario rápido: grid fijo de 4 columnas. Título ocupa 2; detalle
     y acciones ocupan la fila completa cada uno. ── */
  .daily-task-quick-panel { margin-bottom: 10px; padding: 10px; }
  .daily-task-quick-form {
    display: grid;
    grid-template-columns: repeat(4, minmax(150px, 1fr));
    gap: 12px 14px;
    align-items: end;
  }
  .daily-task-title-field { grid-column: span 2; }
  .daily-task-detail-field,
  .daily-task-form-actions { grid-column: 1 / -1; }
  .daily-task-form-actions { display: flex; gap: 8px; justify-content: flex-end; }
  .daily-task-quick-form textarea { resize: none; min-height: 56px; }

  @media (max-width: 1100px) {
    .daily-task-quick-form { grid-template-columns: repeat(2, minmax(150px, 1fr)); }
  }
  @media (max-width: 680px) {
    .daily-task-quick-form { grid-template-columns: minmax(0, 1fr); }
    .daily-task-title-field { grid-column: auto; }
  }

  /* ── Tablero: 4 columnas iguales; cada columna es su propia superficie
     con cabecera fija y lista de tarjetas. ── */
  .daily-board {
    display: grid;
    grid-template-columns: repeat(4, minmax(220px, 1fr));
    gap: 12px;
    margin-top: 12px;
    align-items: start;
  }
  .daily-column {
    min-width: 0;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface-2);
    overflow: hidden;
  }
  .daily-column-head {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 12px;
    border-bottom: 1px solid var(--line);
    background: var(--surface);
  }
  .daily-column-head h3 { font-size: var(--fs-body); }
  .daily-column-head span {
    display: inline-grid; place-items: center; min-width: 24px; height: 24px;
    border-radius: 999px;
    background: var(--ink); color: var(--surface);
    font-size: var(--fs-caption); font-weight: 800;
  }
  .daily-task-list { display: grid; gap: 10px; padding: 10px; }

  @media (max-width: 1100px) {
    .daily-board { grid-template-columns: repeat(2, minmax(240px, 1fr)); }
  }
  @media (max-width: 680px) {
    .daily-board { grid-template-columns: 1fr; }
  }

  /* ── Contexto de agenda del día ── */
  .daily-event-context { margin-top: 12px; }
  .daily-event-panel {
    display: grid; gap: 10px; padding: 14px;
    border: 1px solid var(--line); border-radius: var(--r); background: var(--surface);
  }
  .daily-event-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
  .daily-event-list { display: grid; gap: 8px; }
  .daily-event-item {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 10px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); }
  .daily-event-item > div { display: grid; gap: 4px; min-width: 0; }
  .daily-event-item span { color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }

  .daily-event-item strong, .daily-event-item span,
  .daily-task-summary strong, .daily-task-summary span,
  .daily-task-description, .daily-comment p, .daily-attachment a { overflow-wrap: anywhere; }

  .daily-empty, .daily-thread-empty { color: var(--muted); font-size: var(--fs-small); font-weight: 650; }

  /* ── Tarjeta de tarea: acento de estado a la izquierda + desplegable.
     Sin sombra de reposo (canon: la elevación es solo de overlays). ── */
  .daily-task-card {
    display: grid; min-width: 0; overflow: hidden;
    border: 1px solid var(--line); border-left: 4px solid var(--blue); border-radius: var(--r-sm);
    background: var(--surface);
  }
  .daily-task-card--doing { border-left-color: var(--green); }
  .daily-task-card--blocked { border-left-color: var(--red); }
  .daily-task-card--done { border-left-color: var(--muted-2); opacity: .86; }

  .daily-task-summary {
    display: flex; width: 100%; justify-content: space-between; gap: 10px; min-width: 0;
    padding: 10px 12px; text-align: left;
    transition: background var(--t);
  }
  .daily-task-summary:hover { background: var(--surface-2); }
  .daily-task-summary > div { display: grid; gap: 4px; min-width: 0; }
  .daily-task-summary span, .daily-task-meta { color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
  .daily-task-summary-side { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; }
  .daily-task-chevron {
    display: inline-grid; place-items: center; width: 18px; height: 18px; border-radius: 999px;
    color: var(--muted);
    transition: transform var(--t-lg), background var(--t), color var(--t);
  }
  .daily-task-summary:hover .daily-task-chevron { background: var(--blue-soft); color: var(--blue); }
  .daily-task-card.is-expanded .daily-task-chevron { transform: rotate(180deg); }

  /* Desplegable con alto fluido: cerrado no reserva espacio (0fr). */
  .daily-task-body {
    display: grid; grid-template-rows: 0fr; opacity: 0; visibility: hidden; pointer-events: none;
    transition: grid-template-rows var(--t-lg), opacity var(--t), visibility 0s linear var(--t-lg);
  }
  .daily-task-card.is-expanded .daily-task-body {
    grid-template-rows: 1fr; opacity: 1; visibility: visible; pointer-events: auto; transition-delay: 0s;
  }
  .daily-task-body-inner { display: grid; gap: 10px; min-width: 0; overflow: hidden; padding: 0 12px; }
  .daily-task-card.is-expanded .daily-task-body-inner { padding-bottom: 12px; }

  .daily-priority {
    align-self: start; border-radius: 999px; padding: 4px 8px;
    background: var(--blue-soft); color: var(--blue);
    font-size: var(--fs-caption); font-weight: 700; white-space: nowrap;
  }
  .daily-priority--urgent { background: var(--red-soft); color: var(--red); }
  .daily-priority--high { background: var(--amber-soft); color: var(--amber); }
  .daily-priority--low { background: var(--surface-3); color: var(--muted); }

  .daily-task-description { margin: 0; color: var(--muted); font-size: var(--fs-small); line-height: 1.45; }
  .daily-linked-event {
    display: inline-flex; width: fit-content; align-items: center; border-radius: 999px;
    padding: 4px 8px; background: var(--blue-soft); color: var(--blue);
    font-size: var(--fs-caption); font-weight: 700;
  }
  .daily-task-meta, .daily-task-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
  .daily-task-actions { gap: 6px; }

  /* ── Hilo de comentarios y adjuntos: mismo patrón, dentro de la tarjeta. ── */
  .daily-thread, .daily-attachments { display: grid; gap: 8px; border-top: 1px solid var(--line); padding-top: 10px; }
  .daily-thread-list, .daily-attachment-list { display: grid; gap: 8px; }
  .daily-comment { display: grid; gap: 2px; border-radius: var(--r-sm); padding: 8px; background: var(--surface-2); }
  .daily-comment strong { font-size: var(--fs-caption); }
  .daily-comment span { color: var(--muted); font-size: var(--fs-caption); }
  .daily-comment p { margin: 0; font-size: var(--fs-small); line-height: 1.4; }
  .daily-comment-form, .daily-attachment-form { display: grid; gap: 8px; }
  .daily-comment-form textarea { min-height: 58px; }
  .daily-attachment {
    display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap;
    border-radius: var(--r-sm); padding: 8px; background: var(--surface-2);
  }
  .daily-attachment a { color: var(--blue); font-size: var(--fs-small); font-weight: 700; }
  .daily-attachment span { color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
}

/* líneas: ~209 de presupuesto 150 — excedido a propósito: la tarjeta del
   kanban (columna/tarjeta/prioridad/hilo/adjuntos) no tiene componente
   dueño en 04-componentes (verificado: no está en ningún "Cubre", el único
   candidato -.daily-summary- está MUERTO), así que esta vista es también su
   única definición de identidad visual, no solo su geometría. */
/* ══ usuarios (vista) ═══════════════════════════════════════════════════════
   Vista: "Usuarios y permisos" · #view-users / data-view="users"
   Cubre: .users-layout, .permission-grid, .users-wrap, .users-table (ancho
     mínimo), .user-permissions (ancho + wrap de columna), .users-table
     .table-action-row (variante alineada a la izquierda)
   Fuente v1: styles.css ~L5678-5811 (bloque "USERS" completo) + ~L12744
     (colapso responsive en 760px → consolidado al breakpoint cerrado más
     cercano, 680px).
   NO cubre — ya resuelto en 04-componentes, no re-skinear aquí:
     .table-section/.section-head → panel.css; .table-wrap/.table-action-row
     (receta base) → tabla.css; .user-role/.user-status → badge.css;
     .check-line--switch (switch "Activo") → campo.css; .primary-action/
     .table-action → boton.css.
   Gaps detectados, fuera de esta vista (no se resuelven aquí):
     · .tool-panel (superficie de .user-form) — ya reportado como faltante
       de 04-componentes/panel.css por 05-vistas/pedidos.css y
       cierre-de-turno.css (se usa en ≥8 vistas); no se repite el hallazgo.
     · .permission-check (label+checkbox con borde/fondo/tipografía propios;
       v1 lo pintaba con !important) es una receta visual completa, no
       geometría — falta nombrarla en campo.css junto a .check-line. Aquí
       solo se ubica dentro de la grilla, que sí es de esta vista.
   Cambios visibles: gap de .permission-grid 7px (fuera de escala) → 8px,
     el peldaño más cercano.
   Casos límite: lista de permisos larga en la tabla envuelve dentro de un
     ancho máximo en vez de ensanchar la fila; sin permisos o sin último
     ingreso llegan como texto plano ("Sin permisos" / "—") ya resuelto por
     JS. Bajo 680px el panel de alta deja de caber junto a la tabla: se
     apila arriba de ella.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* ── Dos columnas: formulario fijo (alta/edición) + tabla fluida ─────── */
  .users-layout {
    display: grid;
    grid-template-columns: 320px minmax(0, 1fr);
    gap: 12px;
    align-items: start;
  }

  /* El wrap nace a la misma altura que el form: el margen superior de
     .table-wrap es para cuando cuelga bajo un título, no en este layout. */
  .users-wrap { margin-top: 0; }

  /* Piso de ancho de la tabla: por debajo, .table-wrap entra en su propio
     scroll horizontal antes que las columnas se aplasten. */
  .users-table { min-width: 760px; }

  /* Columna "Permisos": envuelve en vez de estirar la fila a lo ancho. */
  .user-permissions { max-width: 420px; white-space: normal; }

  /* Acciones de esta tabla pegadas a la izquierda, no al borde derecho de
     la receta base de tabla.css (variante de alineación, no re-skin). */
  .users-table .table-action-row { justify-content: flex-start; }

  /* Grilla de checks de permisos, dentro de los 320px del formulario. */
  .permission-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px; /* v1: 7px, fuera de escala */
  }

  /* ── Responsive ──
     v1 colapsaba en 760px; consolidado al breakpoint cerrado más cercano. */
  @media (max-width: 680px) {
    .users-layout { grid-template-columns: 1fr; }
    .permission-grid { grid-template-columns: 1fr; }
  }
}

/* líneas: ~70 de presupuesto 150 */
/* ══ ventas (data-view="sales") ═════════════════════════════════════════════
   Cubre: #view-sales .sales-head, #view-sales .sales-wrap,
          #view-sales .sales-table, #view-sales .item-cell
   Fuente v1: styles.css ~L3606-3610 (.sales-head align-items, fusionada con
          .book-head/.recipe-head/.reports-head/.benchmark-head), ~L5146-5159
          (.sales-wrap/.sales-table — fusionadas con .book-wrap/.recipe-wrap
          y .book-table/.recipe-table; la regla propia de .sales-table gana
          por orden a la genérica de 2280px y deja 1700px como ancho final),
          ~L5214 (.item-cell, fusionada con .supplier-cell/.recipe-table).
   Casos límite: nombre de producto o de grupo (agrupado) largo → .item-cell
          le da ancho de sobra antes de que la fila lo apriete; sin resultados
          o error de carga → fila única con colspan que pinta app.js sobre
          .empty (ya vestida en 02-base, sin regla aquí); venta cancelada →
          .credit-badge.danger (badge.css, esta vista no la toca); dato
          ausente llega como "-" de texto plano desde el JS.
   Nota: .section-head/.sales-summary/.sales-tools/.segmented-control/
          .date-range-filter/.search-box/.table-wrap ya vienen de
          04-componentes (panel/kpi/toolbar) — esta vista no repite su
          geometría. La densidad de celda (12px/7-8px) que v1 aplicaba a
          .book-table/.sales-table/.recipe-table tampoco se porta aquí: es
          un re-skin de la tabla base (CONVENCIONES §8), no geometría de
          vista — la tabla usa la receta canon de 02-base (th 9×11, td
          10×11); igual con el peso 650 que v1 daba a .item-cell, se porta
          el ancho (columna) pero no el font-weight (acento visual).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vistas {

  /* Título arriba a la izquierda: la toolbar trae 8 filtros y puede crecer
     a dos o tres filas — no debe tirar el título al centro vertical. */
  #view-sales .sales-head { align-items: flex-start; }

  /* ── Techo de scroll de la tabla ──────────────────────────────────────
     Lo que se resta es lo que hay ENCIMA de la tabla más el aire de abajo:
     padding del workspace + barra de herramientas + cabecera de sección +
     16 de padding de la tarjeta + 28 de respiro. Medido, no estimado — si
     cambia la cabecera hay que volver a medir (el valor viejo venía del
     header antiguo con eyebrow y tarjetas KPI, y reservaba 120-140px que
     ya no existen: esa franja quedaba en blanco bajo la tabla). */
  #view-sales .sales-wrap { max-height: calc(100dvh - var(--nav-alto) - 206px); }

  /* Con el riel colapsado a barra superior la tabla arranca más abajo. */
  @media (max-width: 900px) {
    #view-sales .sales-wrap { max-height: calc(100dvh - var(--nav-alto) - 324px); }
  }

  /* Boleta a boleta trae 16 columnas en modo detalle (agrupado: 9): sin
     ancho mínimo se aprietan ilegibles antes de que el navegador ofrezca
     scroll horizontal — el min-width es lo que dispara ese scroll. */
  #view-sales .sales-table { min-width: 1700px; }

  /* Producto (detalle) / Grupo (agrupado): única columna de texto libre
     entre columnas de cifras — se le reserva ancho para que no quede
     comprimida ni truncada de más. */
  #view-sales .item-cell { min-width: 300px; }
}

/* líneas: ~49 de presupuesto 150 */
/* ══ utilidades ═══════════════════════════════════════════════════════════
   Cubre: .numeric, .span-2, .span-3, .tone-good, .tone-bad, .tone-warn,
     .tone-muted, .is-positive, .is-negative
   Fuente v1: styles.css L5096 (.numeric), L2649-2650+L2678-2679 (tone-*,
     fusión con price-link), L9690-9691+15885-15886+15990+16036+16250-16251+
     16337-16338+17228-17229+17312-17313 (is-positive/is-negative, fusión
     last-wins de todos los contextos: kpi contable, balance, cuentas propias,
     operación financiera, cartolas banco); L14656-14657+16177 (span-2/
     span-3, fusión: los dos contextos de v1 dan el mismo resultado visual
     porque sus grids padre tienen exactamente 2/3 columnas — span N y
     "ocupar todo" coinciden)
   Estados: sin estados propios — son utilidades de solo lectura (color,
     alineación, posición en grid), no responden a interacción.
   Nota de capa: .is-* y .tone-* NO llevan !important (v1 sí, por necesidad
     de pelear contra selectores de componente más específicos). Al vivir en
     @layer utilidades, que se declara después de componentes/vistas, ganan
     por orden de capa sin importar especificidad — el !important de v1 es
     deuda que muere aquí.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer utilidades {

  /* ── Alineación numérica ─────────────────────────────────────────────────
     Va en <td>/<th> (tabular-nums ya lo resuelve 02-base) y en <input>
     dentro de tablas editables: en ambos casos solo falta el align. */
  .numeric { text-align: right; }

  /* ── Ocupar columnas en un grid ──────────────────────────────────────────
     Genérico: sirve para cualquier grid padre (alta de proveedor, carga de
     cartolas, activos fijos) con 2+ columnas explícitas. Bajo 680px todos
     los grids del canon colapsan a 1 columna: span deja de tener sentido y
     forzarlo evita que el navegador cree columnas implícitas. */
  .span-2 { grid-column: span 2; }
  .span-3 { grid-column: span 3; }

  @media (max-width: 680px) {
    .span-2, .span-3 { grid-column: span 1; }
  }

  /* ── Ancho completo ─────────────────────────────────────────────────────
     Se usa sobre todo en botones de submit (`primary-action wide`) dentro de
     formularios de una columna: el botón ocupa el ancho del campo de arriba. */
  .wide { width: 100%; }

  /* ── Tono semántico de texto (cifras, tendencias, ahorros) ───────────────
     Familia usada suelta o compuesta con .price-link/.price-savings (esas
     variantes visuales viven en el componente, no aquí). */
  .tone-good  { color: var(--green); font-weight: 700; }
  .tone-bad   { color: var(--red);   font-weight: 700; }
  .tone-warn  { color: var(--amber); font-weight: 700; }
  .tone-muted { color: var(--muted); }

  /* ── Signo de cifra (positivo/negativo) ──────────────────────────────────
     El peso de fuente por contexto (kpi, fila de tabla, strip de saldo) es
     decisión del componente que las usa, no de esta utilidad. */
  .is-positive { color: var(--green); }
  .is-negative { color: var(--red); }
}

/* líneas: ~55 de presupuesto 80 */
/* ══ terceros — skin de Tabulator 6.3.1 ══════════════════════════════════════
   Cubre: contenedor .tabulator, cabecera (.tabulator-header/.tabulator-col/
     .tabulator-col-title), sorters (.tabulator-col-sorter/.tabulator-arrow),
     redimensionadores (.tabulator-col-resize-handle), filas y celdas
     (.tabulator-row/.tabulator-cell), columnas frozen (.tabulator-frozen(-left/
     -right)), agrupaciones (.tabulator-group), árbol de datos
     (.tabulator-data-tree-control/-branch), placeholder vacío
     (.tabulator-placeholder-contents), pie (.tabulator-footer), y los
     sub-skins del inventario: .inv-tabulator, .inv-summary-tabulator(--annual),
     .inv-tabulator-total-row, .inv-summary-detail-row.
   Fuente v1: styles.css ~L6873-6924 (receta base) + ~L13454-13466 (capa v117,
     borde de columna transparente + hover) + ~L11510-11970 (sub-skins de
     inventario, subfamilias/plan de compra/resumen anual).
   Estados: reposo · hover (fila, sorter, resizer, grupo) · focus-visible
     (cabecera ordenable) completos. Active/disabled no aplican: Tabulator no
     expone esos estados como clase propia. Loading no aplica: ningún llamado
     usa `showLoader`. Vacío = .tabulator-placeholder-contents (cubierto).
     Error lo resuelve la vista con `.empty`/`.inv-error` en vez de montar la
     tabla — no hay estado de error dentro del propio Tabulator.

   ── Por qué tantos !important ──────────────────────────────────────────────
   tabulator.min.css se carga por <link> SIN capa. En cascada por capas, TODO
   selector sin capa gana a TODO selector dentro de una @layer con prioridad
   normal, sin importar especificidad ni orden (verificado empíricamente: una
   regla `.box{background:red}` sin capa le gana a `div.box.box.box` dentro de
   una capa). La única forma de pintar encima del CDN es `!important` dentro de
   la capa. Se usa SOLO en las propiedades que Tabulator realmente fija en ese
   selector exacto (auditado contra tabulator.min.css); las propiedades nuevas
   (tipografía, tinte, sombra, radio) no lo necesitan porque no compiten con
   nada. No se repite el comentario línea a línea: esta nota es la
   justificación de cada `!important` del archivo.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer terceros {

  /* ── Contenedor: misma receta que .table-wrap (borde + radio + sombra xs).
     overflow:hidden ya lo trae Tabulator por defecto → los bordes cuadrados
     de header/footer quedan recortados al radio del contenedor solos. ── */
  .tabulator {
    border: 1px solid var(--line) !important;
    border-radius: var(--r);
    background: var(--surface) !important;
    box-shadow: var(--shadow-xs);
    font-size: var(--fs-body) !important;
  }

  /* ── Cabecera: banda fría, igual que <th> ─────────────────────────────── */
  .tabulator .tabulator-header {
    background: var(--surface-2) !important;
    border-bottom: 1px solid var(--line) !important;
  }

  .tabulator .tabulator-header .tabulator-col {
    background: var(--surface-2) !important;
    /* v117: separadores entre columnas fuera, igual que <th> sin línea vertical */
    border-right-color: transparent !important;
  }

  /* v1 nunca le dio su propio padding al contenido de cabecera y quedó a los
     4px crudos del CDN — la intención declarada ("misma receta que <th>")
     no se cumplía. Se corrige aquí: mismo padding que la celda de header nativa. */
  .tabulator .tabulator-header .tabulator-col-content { padding: 9px 11px !important; }

  .tabulator .tabulator-header .tabulator-col-title {
    color: var(--muted-2);
    font-size: var(--fs-caption);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
  }

  /* ── Sorters: triángulo de Tabulator recoloreado a tokens ────────────── */
  .tabulator .tabulator-col-sorter .tabulator-arrow {
    border-bottom-color: var(--muted-2) !important;
  }

  .tabulator .tabulator-col[aria-sort="ascending"] .tabulator-col-sorter .tabulator-arrow {
    border-bottom-color: var(--accent) !important;
  }

  .tabulator .tabulator-col[aria-sort="descending"] .tabulator-col-sorter .tabulator-arrow {
    border-top-color: var(--accent) !important;
  }

  /* Columna ordenable: hover propio (reemplaza el gris #cdcdcd del CDN) +
     foco de teclado, que Tabulator no trae de fábrica. */
  .tabulator .tabulator-col.tabulator-sortable:hover {
    background-color: var(--surface-3) !important;
  }

  .tabulator .tabulator-col.tabulator-sortable:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
  }

  /* ── Redimensionador de columna: invisible en reposo (igual que el CDN),
     se enciende con un hilo de acento al pasar el mouse. Propiedad nueva:
     el CDN solo fija `cursor` en :hover, no `background`. ── */
  .tabulator .tabulator-col-resize-handle:hover {
    background: color-mix(in srgb, var(--accent) 45%, transparent);
  }

  /* ── Filas y celdas: línea-soft + banda al hover, igual que <tr>/<td> ──── */
  .tabulator .tabulator-row {
    background: var(--surface) !important;
    border-bottom: 1px solid var(--line-soft) !important;
  }

  /* Tabulator marca cada fila como "seleccionable" aunque no se use selección
     de filas; es el gancho real que usa el hover en producción hoy. */
  .tabulator .tabulator-row.tabulator-selectable:hover {
    background: var(--surface-2) !important;
  }

  /* Zebra del CDN fuera: la tabla nativa no la tiene, Tabulator tampoco debe.
     :not(.tabulator-group) es obligatorio: el índice par/impar de Tabulator
     cuenta también las filas de grupo, así que sin el :not() esta regla le
     ganaba por especificidad al tinte del grupo cuando le tocaba caer en fila
     par (bug real, visto al probar con datos: un grupo de cada dos salía sin
     tinte). Al no calzar el selector en absoluto, deja de competir. */
  .tabulator .tabulator-row.tabulator-row-even:not(.tabulator-group) {
    background: var(--surface) !important;
  }

  /* Sin font-weight propio: hereda 400 del body, igual que <td> (que tampoco
     lo declara). v1 forzaba 520 (fuera de escala) — se retira, no se traduce. */
  .tabulator .tabulator-cell {
    border-right-color: transparent !important;
    padding: 10px 11px !important;
  }

  /* ── Columnas frozen: mismo fondo que su fila (heredado por el propio CDN
     vía `background-color:inherit`), solo se recolorea el filo que separa
     lo fijo de lo que se desliza debajo. ── */
  .tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left,
  .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left {
    border-right-color: var(--line-2) !important;
  }

  .tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-right,
  .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right {
    border-left-color: var(--line-2) !important;
  }

  /* ── Agrupaciones: banda intermedia entre header y fila normal, con el
     triángulo de expandir/colapsar recoloreado a tinta. ── */
  .tabulator-row.tabulator-group {
    background: var(--surface-2) !important;
    border-top-color: var(--line) !important;
    border-bottom-color: var(--line) !important;
    border-right-color: transparent !important;
    padding: 9px 11px !important;
    color: var(--ink-2);
  }

  .tabulator-row.tabulator-group .tabulator-arrow { border-left-color: var(--muted) !important; }
  .tabulator-row.tabulator-group.tabulator-group-visible .tabulator-arrow { border-top-color: var(--muted) !important; }

  /* Insumos del mes agrupados por subfamilia: banda con tinte verde 6% +
     filo de acento, traducción del beige/verde heredado (v1 usaba #f5f3ec
     con un box-shadow inset verde suelto; aquí el tinte y el filo son la
     misma fórmula canon — cambio visible, más limpio). */
  .inv-tabulator .tabulator-group {
    background: color-mix(in srgb, var(--green) 6%, var(--surface-2)) !important;
    box-shadow: inset 3px 0 0 var(--green);
  }

  /* ── Árbol de datos (subfamilia → insumo en el resumen mensual): el CDN
     dibuja una cajita oscura de fábrica; se recolorea a tinta translúcida. ── */
  .tabulator-data-tree-control {
    background: color-mix(in srgb, var(--ink) 10%, transparent) !important;
    border-color: var(--line-2) !important;
  }
  .tabulator-data-tree-control:hover { background: color-mix(in srgb, var(--ink) 16%, transparent) !important; }
  .tabulator-data-tree-control-expand,
  .tabulator-data-tree-control-expand::after,
  .tabulator-data-tree-control-collapse::after { background: var(--ink-2) !important; }
  .tabulator-data-tree-branch { border-color: var(--line-2) !important; }

  /* ── Placeholder vacío: mismo lenguaje que td.empty (canon §5) ────────── */
  .tabulator .tabulator-placeholder-contents {
    color: var(--muted-2) !important;
    font-size: var(--fs-body-lg) !important;
    font-weight: 550 !important;
  }

  /* ── Pie: banda fría, igual que la cabecera. Hoy ninguna tabla usa
     paginación ni fila de cálculo — queda listo para cuando se necesite. ── */
  .tabulator .tabulator-footer {
    background: var(--surface-2) !important;
    border-top: 1px solid var(--line) !important;
    color: var(--muted-2) !important;
  }

  /* Cifras siempre tabulares — la base ya lo hace para <td>/<th>, Tabulator
     no hereda esa regla porque .tabulator-cell no es una celda nativa. */
  .tabulator, .tabulator .tabulator-cell { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

  /* ═══ Sub-skins del inventario ═══════════════════════════════════════════
     Los cuatro contenedores que envuelven instancias concretas de Tabulator
     en la vista Inventario (geometría/contenido de sus celdas vive en la
     vista; aquí solo lo que ajusta el propio grid). ═══════════════════════ */

  /* Insumos del mes: alto acotado a la ventana, filas más compactas que la
     receta base (más densidad = más filas visibles sin scroll). */
  .inv-tabulator {
    height: min(620px, calc(100vh - 330px));
    min-height: 330px;
  }

  .inv-tabulator .tabulator-row .tabulator-cell { padding-top: 5px !important; padding-bottom: 5px !important; }

  /* Resumen de subfamilias / plan de compra / anual: sin borde ni sombra
     propios (los pone el panel que lo envuelve), filas aún más densas. */
  .inv-summary-tabulator {
    min-height: 0;
    background: var(--surface);
  }

  .inv-summary-tabulator .tabulator { border: 0 !important; border-radius: 0; box-shadow: none; }
  .inv-summary-tabulator .tabulator-row .tabulator-cell { padding-top: 5px !important; padding-bottom: 5px !important; }
  .inv-summary-tabulator .tabulator-row { min-height: 30px !important; }
  .inv-summary-tabulator .tabulator-header .tabulator-col-title { color: var(--ink); font-weight: 800; }

  /* Tabulator fija min-height por JS (estilo inline) en el tableholder
     cuando la data cabe sin scroll; sin !important el inline gana siempre. */
  .inv-summary-tabulator .tabulator-tableholder { min-height: 0 !important; }

  .inv-summary-tabulator--annual { height: 460px; }

  /* Fila de detalle (insumo suelto bajo su subfamilia en el árbol): banda
     tenue, coincide con el hover — no hace falta pelear su prioridad. */
  .inv-summary-detail-row .tabulator-cell {
    background: var(--surface-2) !important;
    color: var(--muted);
    font-size: var(--fs-small);
  }
  .inv-summary-detail-row .tabulator-cell:first-child { font-weight: 650; }

  /* Fila de total: tinte verde 6% (misma fórmula que el grupo de arriba).
     Se pinta la CELDA, no la fila — por eso no compite con el hover de fila
     (la celda es opaca y tapa lo que haya debajo). El !important solo hace
     falta porque la primera columna suele venir frozen y esa sí trae
     `background-color:inherit` de fábrica. */
  .inv-tabulator-total-row .tabulator-cell {
    border-top: 1px solid var(--line-2);
    background: color-mix(in srgb, var(--green) 6%, var(--surface)) !important;
    color: var(--ink);
    font-weight: 800;
  }

  /* ── Contenido enriquecido de celdas del inventario ───────────────── */
  .inv-money-pct { display: grid; gap: 2px; justify-items: end; line-height: 1.1; }
  .inv-money-pct strong { color: var(--ink); font-size: var(--fs-small); font-weight: 700; }
  .inv-money-pct small { color: var(--muted); font-size: var(--fs-caption); font-weight: 700; }
  .inv-money-pct.is-empty { display: inline; color: var(--muted); }
  .inv-neg,
  .inv-money-pct.inv-neg strong { color: var(--red); font-weight: 700; }

  .inv-plan-action { display: grid; gap: 2px; align-content: center; min-height: 30px; padding-left: 8px; border-left: 4px solid var(--line-strong); }
  .inv-plan-action strong { color: var(--ink); font-size: var(--fs-small); line-height: 1.1; }
  .inv-plan-action--now { border-left-color: var(--rose); }
  .inv-plan-action--week { border-left-color: var(--amber); }
  :is(.inv-plan-action--pending, .inv-plan-action--soon) { border-left-color: var(--blue); }
  .inv-plan-action--planned { border-left-color: var(--green); }
  :is(.inv-plan-action--inactive, .inv-plan-action--snoozed) { border-left-color: var(--muted); }
  :is(.inv-plan-action--dormant, .inv-plan-action--review) { border-left-color: var(--muted-2); }
  .inv-plan-action--archived { border-left-color: var(--line-strong); }

  .inv-plan-row--comprar_ahora .tabulator-cell { background: color-mix(in srgb, var(--rose) 5%, var(--surface)) !important; }
  .inv-plan-row--esta_semana .tabulator-cell { background: color-mix(in srgb, var(--amber) 5%, var(--surface)) !important; }
  .inv-plan-row--pedido_pendiente .tabulator-cell { background: color-mix(in srgb, var(--blue) 5%, var(--surface)) !important; }
  :is(.inv-plan-row--pospuesto, .inv-plan-row--revisar_inactivo,
      .inv-plan-row--dormido, .inv-plan-row--historico_muerto) .tabulator-cell {
    background: color-mix(in srgb, var(--muted) 4%, var(--surface)) !important;
  }
  .inv-plan-row--historico_muerto .tabulator-cell { color: var(--muted); }

  .inv-plan-group-header { display: inline-flex; align-items: center; gap: 8px; max-width: 100%; color: var(--ink); font-size: var(--fs-small); font-weight: 800; }
  .inv-plan-group-header strong { overflow: hidden; max-width: min(460px, 46vw); text-overflow: ellipsis; white-space: nowrap; }
  .inv-plan-group-header small { display: inline-flex; min-width: 24px; justify-content: center; padding: 2px 7px; border-radius: 999px; background: var(--surface); color: var(--muted); font-size: var(--fs-caption); font-weight: 700; }
  .inv-plan-group-header--supplier small.is-now { background: color-mix(in srgb, var(--rose) 9%, var(--surface)); color: var(--rose); }
  .inv-plan-group-header--supplier small.is-week { background: color-mix(in srgb, var(--amber) 10%, var(--surface)); color: var(--amber); }
  .inv-plan-group-header--supplier small.is-pending { background: color-mix(in srgb, var(--blue) 9%, var(--surface)); color: var(--blue); }
  .inv-plan-reason { display: block; color: var(--ink-2); font-size: var(--fs-caption); font-weight: 650; line-height: 1.25; white-space: normal; }
  .inv-plan-decisions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 6px; }

  .inv-item-name { display: block; overflow: hidden; color: var(--ink); font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }
  .inv-item-meta { display: block; overflow: hidden; margin-top: 2px; color: var(--muted); font-size: var(--fs-caption); font-weight: 550; text-overflow: ellipsis; white-space: nowrap; }
  .inv-group-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
  .inv-group-title { overflow: hidden; color: var(--ink); font-size: var(--fs-small); font-weight: 700; text-overflow: ellipsis; white-space: nowrap; }
  .inv-group-count { flex: none; padding: 2px 7px; border-radius: 999px; background: var(--surface-3); color: var(--muted); font-size: var(--fs-caption); font-weight: 650; }
  .inv-cat-total { margin-left: auto; color: var(--muted); font-size: var(--fs-caption); font-weight: 550; }

  .inv-editable,
  .inv-editable-cell { position: relative; display: block; min-height: 22px; border-radius: var(--r-xs); cursor: pointer; }
  .inv-editable:hover,
  .inv-editable-cell:hover { background: var(--blue-soft) !important; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 20%, transparent); }
  .inv-placeholder { color: var(--muted-2); }
  .inv-inline-input { width: min(96px, 100%); min-width: 72px; padding: 4px 6px; border: 1px solid var(--accent); border-radius: var(--r-xs); background: var(--surface); color: var(--ink); font: inherit; text-align: right; outline: none; box-shadow: var(--focus); }
  .inv-delete-btn { display: inline-grid; place-items: center; width: 28px; height: 28px; padding: 0; border: 1px solid transparent; border-radius: var(--r-xs); background: transparent; color: var(--muted-2); cursor: pointer; font-size: var(--fs-h3); }
  .inv-delete-btn:hover { border-color: color-mix(in srgb, var(--red) 24%, transparent); background: var(--red-soft); color: var(--red); }
}

/* líneas: ~253 de presupuesto 400 */
/* ══ 08 · PRINT — documentos de papel ═══════════════════════════════════════
   Cubre: .hr-slip-* completo (liquidación de sueldo: vitrina de pantalla +
     documento + @media print), .hr-certificate-* completo (certificado
     laboral, cuerpo en Georgia serif) y TODAS las .annex-* de styles.css que
     no viven ya en un componente (el anexo de catering es una página aparte,
     catering_anexo.php, con su propia pantalla y su propio print).
   NO cubre (ya tienen receta única en 04-componentes, no se duplica aquí):
     .annex-kpi-grid / .annex-match-summary → kpi.css
     .annex-badge / .annex-status → badge.css
     .annex-controls (grid/gap/padding) / .annex-file-control → toolbar.css
     .annex-empty → feedback.css
     Tabla cruda (th/td/hover) de .annex-total-table/.annex-ingredient-table
     → 02-base.css (son <table> planas, la base ya las resuelve).
   Fuente v1: hr.css L379-461 (documento) + L593-602 (720px→680px) +
     L611-619 (print+@page); styles.css L12355-12798 (anexo, fusionado con
     L12800-13232 donde el sistema KPI/toolbar ya adoptó el anexo — esa parte
     no se re-porta aquí) + L13234-13328 (print+@page; se excluye
     body.recipe-tech-printing: no es clase .annex-/.hr-slip-/.hr-certificate-,
     pertenece al módulo de recetas — fuera de este archivo).
   Licencia de color (única excepción a "todo color es var()"): dentro de
     .hr-slip y .hr-certificate el texto/fondo propios del PAPEL (tinta de
     cuerpo, caja de líquido, aviso, línea de firma) son "color de documento
     impreso" — se quedan en hex crudo, fieles a v1, marcados una sola vez
     aquí. Lo que SÍ se alinea a token es la marca: eyebrows, etiquetas de
     partes y cabecera de caja pasan a var(--brand)/var(--blue-soft). Sombras
     de papel usan color-mix(var(--ink)) en vez de rgba(30,43,61,·): mismo
     desenfoque de v1, color tokenizado.
   Estados: los tres son documentos de solo lectura (hover/focus/active/
     disabled no aplican; no hay loading ni vacío — hr.js siempre entrega
     datos calculados). El anexo SÍ tiene controles interactivos, pero su
     estado vive en 02-base/campo.css/toolbar.css, no aquí.
   Cambios visibles: tipografías <11px suben al piso de escala (7-9.5px→
     var(--fs-caption)); pesos 750/760/690 → 700; radios 8-9px → var(--r-sm)/
     var(--r); paddings/márgenes impares de v1 (5,7,9,11,13,18,22,30,35,54,
     60,62,72px…) al escalón legal más cercano (empate → sube); título del
     certificado 34px→29px (techo de escala); alto de .annex-price-input/
     .annex-match-input 31px→32px (--control-h-compact).
   ═══════════════════════════════════════════════════════════════════════════ */

@layer print {

  /* ════════════════════════════════════════════════════════════════════
     LIQUIDACIÓN DE SUELDO · .hr-slip-*
     ════════════════════════════════════════════════════════════════════ */

  /* Vitrina: mesa fría detrás del papel (compartida con el certificado) */
  .hr-slip-preview,
  .hr-certificate-preview {
    max-height: calc(100vh - 190px);
    overflow: auto;
    padding: 24px;
    background: var(--surface-3);
  }

  .hr-slip {
    max-width: 900px;
    margin: 0 auto;
    padding: 40px;
    background: var(--surface);
    box-shadow: 0 10px 35px color-mix(in srgb, var(--ink) 12%, transparent);
    color: #202b40;                 /* color de documento impreso */
    font-family: var(--sans);
  }

  .hr-slip-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    padding-bottom: 20px;
    border-bottom: 2px solid #1e314f;   /* color de documento impreso */
  }
  .hr-slip-brand {
    color: var(--brand);
    font-size: var(--fs-caption);
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
  }
  .hr-slip-header h1 {
    margin: 8px 0 6px;
    font-family: var(--font-numeric);
    font-size: var(--fs-title);
    font-weight: 800;
    line-height: 1.1;
  }
  .hr-slip-header p { margin: 0; color: #697486; text-transform: capitalize; } /* documento */

  .hr-slip-folio { display: grid; gap: 4px; text-align: right; }
  .hr-slip-folio span,
  .hr-slip-folio small { color: #7d8797; font-size: var(--fs-caption); text-transform: uppercase; } /* documento */
  .hr-slip-folio strong { font-size: var(--fs-small); }

  .hr-slip-parties { display: grid; grid-template-columns: 1fr 1fr; gap: 32px; padding: 24px 0; }
  .hr-slip-parties > div > span {
    color: var(--brand);
    font-size: var(--fs-caption);
    font-weight: 800;
    letter-spacing: .08em;
    text-transform: uppercase;
  }
  .hr-slip-parties h2 {
    margin: 6px 0 12px;
    font-family: var(--font-numeric);
    font-size: var(--fs-body-lg);
    text-transform: uppercase;
  }
  .hr-slip-parties dl {
    display: grid;
    grid-template-columns: 100px 1fr;
    row-gap: 6px;
    margin: 0;
    font-size: var(--fs-caption);
  }
  .hr-slip-parties dt { color: #778193; }                       /* documento */
  .hr-slip-parties dd { margin: 0; font-weight: 650; }

  .hr-slip-attendance {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    margin-bottom: 20px;
    border-radius: var(--r);
    background: var(--surface-2);
  }
  .hr-slip-attendance div {
    display: grid;
    gap: 4px;
    padding: 12px 14px;
    text-align: center;
    border-right: 1px solid var(--line);
  }
  .hr-slip-attendance div:last-child { border: 0; }
  .hr-slip-attendance strong { font-size: var(--fs-body); }

  .hr-slip-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
  .hr-slip-box { overflow: hidden; border: 1px solid var(--line); border-radius: var(--r); }
  .hr-slip-box h3 {
    margin: 0;
    padding: 10px 14px;
    background: var(--blue-soft);
    color: var(--brand);
    font-size: var(--fs-caption);
    letter-spacing: .05em;
    text-transform: uppercase;
  }

  .hr-slip-row, .hr-slip-subtotal, .hr-slip-total {
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: 28px;
    padding: 6px 12px;
    font-size: var(--fs-caption);
    border-bottom: 1px solid var(--line-soft);
  }
  .hr-slip-row span { display: grid; gap: 2px; }
  .hr-slip-row small { color: #929aa7; font-size: var(--fs-caption); }   /* documento */
  .hr-slip-subtotal { background: var(--surface-2); font-weight: 700; }
  .hr-slip-total { background: var(--surface-3); border: 0; font-size: var(--fs-caption); font-weight: 800; }

  /* Etiquetas grises repetidas entre asistencia y bases: misma receta */
  .hr-slip-attendance span,
  .hr-slip-bases span {
    color: #778193;                                              /* documento */
    font-size: var(--fs-caption);
    font-weight: 700;
    text-transform: uppercase;
  }

  .hr-slip-bases {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    margin-top: 16px;
    border: 1px solid var(--line);
    border-radius: var(--r);
  }
  .hr-slip-bases div { display: grid; gap: 4px; padding: 10px 12px; border-right: 1px solid var(--line); }
  .hr-slip-bases div:last-child { border: 0; }
  .hr-slip-bases strong { font-size: var(--fs-caption); }

  .hr-slip-net {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 16px;
    padding: 14px 16px;
    border-radius: var(--r);
    background: #1e314f;            /* color de documento impreso */
    color: var(--surface);
  }
  .hr-slip-net span { font-size: var(--fs-caption); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; }
  .hr-slip-net strong { font-family: var(--font-numeric); font-size: var(--fs-h2); font-weight: 800; line-height: 1; }

  .hr-slip-employer { margin-top: 12px; padding: 10px 12px; border-radius: var(--r-sm); background: var(--surface-2); }
  .hr-slip-employer summary { cursor: pointer; font-size: var(--fs-caption); font-weight: 700; text-transform: uppercase; }
  .hr-slip-employer .hr-slip-row,
  .hr-slip-employer .hr-slip-subtotal { padding-left: 0; padding-right: 0; }

  .hr-slip-warnings {
    margin-top: 14px;
    padding: 10px 12px;
    border-left: 3px solid var(--amber);
    border-radius: var(--r-xs);
    background: var(--amber-soft);
    color: var(--amber);
    font-size: var(--fs-caption);
    line-height: 1.4;
  }
  .hr-slip-warnings ul { margin: 4px 0 0; padding-left: 16px; }

  .hr-slip-variable { margin-top: 14px; padding: 12px; border: 1px solid var(--line); border-radius: var(--r-sm); }
  .hr-slip-variable h3 { margin: 0 0 2px; font-size: var(--fs-caption); text-transform: uppercase; }
  .hr-slip-variable > p { margin: 0 0 8px; color: #697486; font-size: var(--fs-caption); }   /* documento */
  .hr-slip-variable table { width: 100%; border-collapse: collapse; font-size: var(--fs-caption); }
  .hr-slip-variable th, .hr-slip-variable td { padding: 6px; text-align: left; border-top: 1px solid var(--line-soft); }
  .hr-slip-variable th { color: #697486; text-transform: uppercase; }   /* documento */

  .hr-slip-footer {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    align-items: flex-end;
    gap: 32px;
    margin-top: 24px;
    padding-top: 20px;
    border-top: 1px solid var(--line);
  }
  .hr-slip-footer p { margin: 0 0 32px; color: #626d7e; font-size: var(--fs-caption); line-height: 1.5; }   /* documento */
  .hr-slip-footer > div:first-child > span {
    display: block;
    padding-top: 6px;
    border-top: 1px solid #384354;    /* color de documento impreso: línea de firma */
    text-align: center;
    font-size: var(--fs-caption);
  }
  .hr-slip-verify { display: grid; gap: 4px; padding: 12px; border-radius: var(--r-sm); background: var(--surface-2); }
  .hr-slip-verify span, .hr-slip-verify small { color: #7c8695; font-size: var(--fs-caption); text-transform: uppercase; }   /* documento */
  .hr-slip-verify strong { font-size: var(--fs-caption); }

  /* ════════════════════════════════════════════════════════════════════
     CERTIFICADO LABORAL · .hr-certificate-*
     ════════════════════════════════════════════════════════════════════ */

  /* Tarjeta de repaso ANTES del certificado: UI de la app, no papel → tokens */
  .hr-certificate-request-summary {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 2px;
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface-2);
  }
  .hr-certificate-request-summary > div { display: grid; gap: 4px; padding: 16px; background: var(--surface); }
  .hr-certificate-request-summary span {
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
  }
  .hr-certificate-request-summary strong { font-size: var(--fs-body); }
  .hr-certificate-request-summary small { color: var(--muted); font-size: var(--fs-caption); }

  .hr-certificate {
    display: flex;
    flex-direction: column;
    width: min(800px, 100%);
    min-height: 960px;
    margin: 0 auto;
    padding: 48px 48px 40px;
    background: var(--surface);
    box-shadow: 0 12px 38px color-mix(in srgb, var(--ink) 13%, transparent);
    color: #202b40;                 /* color de documento impreso, mismo que el recibo */
    font-family: var(--sans);
  }
  .hr-certificate-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--line);
  }
  .hr-certificate-header > div { display: grid; gap: 4px; }
  .hr-certificate-header > div:last-child { text-align: right; }
  .hr-certificate-header span {
    color: var(--brand);
    font-size: var(--fs-small);
    font-weight: 800;
    letter-spacing: .05em;
    text-transform: uppercase;
  }
  .hr-certificate-header small { color: #798493; font-size: var(--fs-caption); }   /* documento */
  .hr-certificate-header strong { font-size: var(--fs-caption); }

  .hr-certificate-title { padding: 48px 0; text-align: center; }
  .hr-certificate-title p {
    margin: 0 0 10px;
    color: var(--brand);
    font-size: var(--fs-caption);
    font-weight: 800;
    letter-spacing: .18em;
    text-transform: uppercase;
  }
  .hr-certificate-title h1 {
    margin: 0 0 14px;
    color: #17263c;                 /* color de documento impreso: titular */
    font-family: var(--font-numeric);
    font-size: var(--fs-title);
    font-weight: 800;
    line-height: 1.12;
    letter-spacing: -.02em;
    text-transform: uppercase;
  }
  .hr-certificate-title span { color: #697586; font-size: var(--fs-caption); text-transform: capitalize; }   /* documento */

  .hr-certificate-addressee {
    margin: 0 0 28px;
    padding: 8px 12px;
    border-left: 3px solid var(--brand);
  }
  .hr-certificate-addressee span {
    color: #7b8594;                 /* documento */
    font-size: var(--fs-caption);
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
  }
  .hr-certificate-addressee strong { font-size: var(--fs-caption); }

  /* Cuerpo del certificado: Georgia se mantiene tal cual (decisión de marca
     del documento, no del sistema — el resto de la app usa var(--serif)). */
  .hr-certificate-body { font-family: Georgia, "Times New Roman", serif; font-size: var(--fs-body-lg); line-height: 1.85; text-align: justify; }
  .hr-certificate-body p { margin: 0 0 20px; }

  .hr-certificate-signature { display: flex; justify-content: flex-end; margin-top: auto; padding: 48px 20px 40px; }
  .hr-certificate-signature > div {
    display: grid;
    gap: 4px;
    min-width: 300px;
    padding-top: 10px;
    border-top: 1px solid #344052;   /* color de documento impreso: línea de firma */
    text-align: center;
  }
  .hr-certificate-signature span {
    margin-top: -28px;
    padding-bottom: 20px;
    color: #7c8694;                 /* documento */
    font-size: var(--fs-caption);
    text-transform: uppercase;
  }
  .hr-certificate-signature strong { font-size: var(--fs-small); text-transform: uppercase; }
  .hr-certificate-signature small { color: #687386; font-size: var(--fs-caption); }   /* documento */

  .hr-certificate-footer {
    display: grid;
    gap: 4px;
    padding-top: 12px;
    border-top: 1px solid var(--line);
    color: #818b98;                 /* documento */
    font-size: var(--fs-caption);
    line-height: 1.4;
    text-align: center;
  }

  /* ── Responsive (v1: 720px → consolidado a 680px) ─────────────────────── */
  @media (max-width: 680px) {
    .hr-slip-preview,
    .hr-certificate-preview { padding: 6px; }

    .hr-slip { padding: 24px 20px; }
    .hr-slip-parties,
    .hr-slip-columns,
    .hr-slip-footer { grid-template-columns: 1fr; }
    .hr-slip-attendance,
    .hr-slip-bases { grid-template-columns: repeat(2, 1fr); }

    .hr-certificate { min-height: 100vh; padding: 32px 24px; }
    .hr-certificate-request-summary { grid-template-columns: 1fr; }
    .hr-certificate-title { padding: 40px 0 32px; }
    .hr-certificate-signature > div { min-width: 240px; }
  }

  /* ── Impresión: liquidación + certificado comparten hoja A4 ──────────── */
  @media print {
    .hr-slip { max-width: none; padding: 8mm 10mm; box-shadow: none; page: hr-document; }
    .hr-slip-header h1 { font-size: var(--fs-h2); }
    .hr-slip-warnings, .hr-slip-employer { break-inside: avoid; }

    .hr-certificate { width: auto; height: 271mm; min-height: 271mm; padding: 14mm 16mm 10mm; box-shadow: none; page: hr-document; }
    .hr-certificate-title { padding: 20mm 0 14mm; }
    .hr-certificate-signature { break-inside: avoid; }

    @page hr-document { margin: 8mm; size: A4 portrait; }
  }

  /* ════════════════════════════════════════════════════════════════════
     ANEXO DE COTIZACIÓN CATERING · catering_anexo.php (.annex-*)
     Página aparte (no vive en el workspace de la app); su cabecera propia,
     su grilla de trabajo y su tabla de insumos van aquí completas. Las
     piezas que ya son un componente general (KPI, badge, controles de
     toolbar, vacío) NO se repiten — ver nota de cabecera del archivo.
     ════════════════════════════════════════════════════════════════════ */

  .annex-page {
    min-height: 100vh;
    background:
      linear-gradient(180deg, color-mix(in srgb, var(--surface) 76%, transparent),
        color-mix(in srgb, var(--surface-3) 96%, transparent) 260px),
      var(--bg);
  }

  .annex-topbar {
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 14px clamp(16px, 3vw, 32px);
    border-bottom: 1px solid var(--line);
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    backdrop-filter: blur(16px);
  }

  .annex-brand { display: inline-flex; align-items: center; gap: 10px; color: var(--ink); text-decoration: none; }
  .annex-brand span:last-child { display: grid; line-height: 1.15; }
  /* La marca "c•" es blanca (receta de riel en 03-layout): sobre la banda
     clara del anexo necesita su propia loseta oscura para no desaparecer. */
  .annex-brand .brand-mark { background: var(--nav); border-radius: var(--r-sm); }

  /* Etiquetas grises menores repetidas por todo el anexo (marca, cabeceras
     de producto, subtotales, tabla de insumos) */
  .annex-brand small,
  .annex-product-head span,
  .annex-product-totals small,
  .annex-category-total small,
  .annex-ingredient-table small {
    color: var(--muted);
    font-size: var(--fs-caption);
    font-weight: 650;
  }

  .annex-actions,
  .annex-control-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

  .annex-shell { width: min(1540px, calc(100vw - 28px)); margin: 0 auto; padding: 28px 0 48px; }

  .annex-heading { display: flex; align-items: end; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
  .annex-heading h1 { margin: 4px 0 0; line-height: 1.08; }   /* tamaño: fs-title de base ya calza (30px→29px) */

  /* Columna de control + contenido; el ancho de la aside es geometría fija
     del anexo (no la trae toolbar.css, que solo da el ritmo interno) */
  .annex-workspace { display: grid; grid-template-columns: 286px minmax(0, 1fr); align-items: start; gap: 14px; }

  /* Superficie compartida por aside/totales (misma receta que .table-section
     de panel.css; se repite aquí porque .annex-panel no es de mi archivo
     asignado — no se edita panel.css desde aquí) */
  .annex-panel { border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }

  /* El aside flota bajo la topbar sticky (82px ≈ su alto); toolbar.css deja
     esta posición fuera a propósito ("geometría del anexo") */
  .annex-controls { position: sticky; top: 82px; }

  .annex-content { display: grid; gap: 14px; min-width: 0; }
  .annex-totals { overflow-x: auto; }
  .annex-total-table,
  .annex-ingredient-table { width: 100%; border-collapse: collapse; }
  .annex-total-table .numeric,
  .annex-ingredient-table .numeric { text-align: right; }

  .annex-category { display: grid; gap: 10px; }
  .annex-category + .annex-category { margin-top: 14px; }
  .annex-category-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px;
    border: 1px solid var(--line);
    border-radius: var(--r);
    background: var(--surface-2);
  }
  .annex-category-head h2 { margin: 0; }   /* fs-h3 de base ya calza (18px→17px) */
  .annex-category-total { display: grid; gap: 2px; text-align: right; }
  .annex-category-total strong { font-size: var(--fs-h3); }

  .annex-product { overflow: hidden; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); }
  .annex-product-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px; border-bottom: 1px solid var(--line); }
  .annex-product-head h3 { margin: 0; font-size: var(--fs-h3); line-height: 1.2; }
  .annex-product-totals { display: grid; gap: 2px; min-width: 128px; text-align: right; }
  .annex-product-totals strong { font-size: var(--fs-h3); }
  .annex-product-grid { overflow-x: auto; }
  .annex-ingredient-table { min-width: 1120px; }
  .annex-ingredient-table td:first-child { min-width: 230px; }
  .annex-ingredient-table td:first-child strong,
  .annex-ingredient-table td:first-child small { display: block; }

  /* Inputs compactos dentro de la fila de insumo: 32px, no los 40px de base */
  .annex-price-input { max-width: 104px; height: var(--control-h-compact); padding: 0 8px; text-align: right; }
  .annex-match-input { width: 190px; height: var(--control-h-compact); padding: 0 8px; }

  .annex-product-footer { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--line); background: var(--surface-2); }
  .annex-product-footer span {
    padding: 4px 8px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface);
    color: var(--ink-2);
    font-size: var(--fs-small);
    font-weight: 700;
  }

  /* ── Responsive (v1: 760px → consolidado a 680px) ─────────────────────── */
  @media (max-width: 680px) {
    .annex-topbar,
    .annex-heading,
    .annex-product-head,
    .annex-category-head { align-items: stretch; flex-direction: column; }
    .annex-actions { width: 100%; }
    .annex-actions > * { flex: 1 1 130px; }
    .annex-shell { width: min(100% - 20px, 1540px); padding-top: 20px; }
    .annex-heading h1 { font-size: var(--fs-h2); }
    .annex-workspace { grid-template-columns: 1fr; }
    .annex-controls { position: static; }
    .annex-product-totals,
    .annex-category-total { text-align: left; }
  }

  /* ── Impresión: solo el papel de precios, sin chrome de la app ────────── */
  @media print {
    body.annex-page { background: var(--surface); page: annex-document; }
    body.annex-page .annex-topbar,
    body.annex-page .annex-controls,
    body.annex-page .toast { display: none; }
    body.annex-page .annex-shell { width: 100%; padding: 0; }
    body.annex-page .annex-workspace { display: block; }
    body.annex-page .annex-product,
    body.annex-page .annex-category-head,
    body.annex-page .annex-kpi-grid article,
    body.annex-page .annex-panel { box-shadow: none; break-inside: avoid; }
    body.annex-page .annex-ingredient-table { min-width: 0; font-size: var(--fs-caption); }
    body.annex-page .annex-price-input { border: 0; padding: 0; text-align: right; }

    @page annex-document { size: A4; margin: 12mm; }
  }

  /* ── Ficha técnica de receta: modal en pantalla, hoja limpia al imprimir */
  @media print {
    body.recipe-tech-printing { background: var(--surface); }
    body.recipe-tech-printing * { visibility: hidden; }
    body.recipe-tech-printing #recipeTechnicalSheetModal,
    body.recipe-tech-printing #recipeTechnicalSheetModal * { visibility: visible; }
    body.recipe-tech-printing #recipeTechnicalSheetModal {
      position: static;
      display: block;
      padding: 0;
      background: var(--surface);
      page: recipe-technical;
    }
    body.recipe-tech-printing .recipe-technical-modal {
      width: 100%;
      max-height: none;
      overflow: visible;
      padding: 0;
      border: 0;
      box-shadow: none;
    }
    body.recipe-tech-printing .recipe-technical-actions { display: none; }
    body.recipe-tech-printing .recipe-technical-header { grid-template-columns: minmax(0, 1fr) 45mm; }
    body.recipe-tech-printing .recipe-technical-photo { min-height: 36mm; }
    body.recipe-tech-printing .recipe-technical-kpis { grid-template-columns: repeat(4, 1fr); }
    body.recipe-tech-printing :is(.recipe-technical-section, .recipe-technical-box, .recipe-technical-kpis article) { break-inside: avoid; }

    @page recipe-technical { size: A4; margin: 12mm; }
  }
}

/* líneas: ~551 de presupuesto 400 (+151: excede a propósito, mismo criterio
   que toolbar.css/feedback.css — el archivo real son TRES documentos
   completos (liquidación, certificado, anexo de catering con sus 20+
   selectores propios), no un componente. El anexo ya se acotó a las clases
   sin receta propia (kpi/badge/toolbar/feedback la tienen y no se duplican
   aquí); lo que queda es la estructura mínima de cada papel — recortar más
   exigiría partir el archivo en 3, fuera del alcance de esta asignación) */
