/* ==========================================================================
   SCRAP360 DESIGN SYSTEM
   --------------------------------------------------------------------------
   Direction: "Instrumento industrial" — the app is a measuring instrument for
   a metal yard, not a marketing site. Flat surfaces, hairline borders,
   decisive type, tabular numerals, one accent colour. No gradient washes,
   no pastel confetti, minimal shadow.

   Load order: bootstrap -> style.css (legacy) -> scrap360.css (this file).
   This layer owns every visual decision. See docs/DESIGN_SYSTEM.md.
   ========================================================================== */

/* ==========================================================================
   1. TOKENS
   ========================================================================== */

:root {
    /* --- Neutral ramp (cool graphite / steel) --------------------------- */
    --s-n0:   #ffffff;
    --s-n25:  #fafbfc;
    --s-n50:  #f4f6f8;
    --s-n100: #eaeef2;
    --s-n200: #dbe1e8;
    --s-n300: #c2cad4;
    --s-n400: #97a1ae;
    --s-n500: #6b7683;
    --s-n600: #4d5765;
    --s-n700: #374150;
    --s-n800: #232c39;
    --s-n900: #131a24;

    /* --- Primary — actions, links, selection ---------------------------- */
    --s-p50:  #eaf3fc;
    --s-p100: #d1e5f8;
    --s-p200: #a6cbf2;
    --s-p300: #6aa9e6;
    --s-p400: #2b83d6;
    --s-p500: #0b6bcb;
    --s-p600: #0a58a6;
    --s-p700: #084681;

    /* --- Brass — brand mark + "needs attention" only ------------------- */
    --s-brass50:  #fdf7ea;
    --s-brass100: #f8ecd2;
    --s-brass300: #d9a93f;
    --s-brass500: #a9761b;
    --s-brass700: #7c560f;

    /* --- Semantic ------------------------------------------------------- */
    --s-ok:        #11785a;
    --s-ok-tint:   #e6f4ef;
    --s-ok-line:   #bde0d3;
    --s-warn:      #b45309;
    --s-warn-tint: #fdf1e3;
    --s-warn-line: #f2d6b3;
    --s-bad:       #b42318;
    --s-bad-tint:  #fdeceb;
    --s-bad-line:  #f4c9c5;
    --s-info:      var(--s-p500);
    --s-info-tint: var(--s-p50);
    --s-info-line: var(--s-p100);

    /* --- Semantic surface roles ---------------------------------------- */
    --s-canvas:      var(--s-n50);
    --s-surface:     var(--s-n0);
    --s-surface-alt: var(--s-n25);
    --s-sunken:      var(--s-n50);
    --s-line:        var(--s-n200);
    --s-line-strong: var(--s-n300);
    --s-text:        var(--s-n900);
    --s-text-soft:   var(--s-n600);
    --s-text-muted:  var(--s-n500);
    --s-text-invert: var(--s-n0);

    /* --- Type -----------------------------------------------------------
       One family across the whole product, the way business software reads.
       IBM Plex Sans is an enterprise UI face with true tabular figures. */
    --s-font-display: "IBM Plex Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
    --s-font-body: "IBM Plex Sans", "Segoe UI", system-ui, -apple-system, sans-serif;
    --s-font-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", monospace;

    --s-fs-micro: 0.6875rem; /* 11px — column headers, eyebrow */
    --s-fs-xs:    0.75rem;   /* 12px — meta, helper */
    --s-fs-sm:    0.8125rem; /* 13px — table body */
    --s-fs-base:  0.875rem;  /* 14px — UI default */
    --s-fs-md:    1rem;      /* 16px — inputs (prevents iOS zoom) */
    --s-fs-lg:    1.0625rem; /* 17px — card titles */
    --s-fs-xl:    1.25rem;   /* 20px — section titles */
    --s-fs-2xl:   1.5rem;    /* 24px — page title */
    --s-fs-3xl:   1.875rem;  /* 30px — stat values */

    /* --- Space (4px scale) ---------------------------------------------- */
    --s-1: 0.25rem;
    --s-2: 0.5rem;
    --s-3: 0.75rem;
    --s-4: 1rem;
    --s-5: 1.25rem;
    --s-6: 1.5rem;
    --s-8: 2rem;
    --s-10: 2.5rem;
    --s-12: 3rem;

    /* --- Radius (only four) --------------------------------------------- */
    --s-r-sm: 4px;    /* controls: inputs, buttons */
    --s-r-md: 6px;    /* cards, tables */
    --s-r-lg: 8px;    /* page-level panels */
    --s-r-pill: 999px;/* status pills only */

    /* --- Defined component sizes ----------------------------------------
       Every control and row has one declared height, so a screen built from
       these parts lines up without anyone eyeballing padding. */
    --s-row-h: 40px;         /* table body row */
    --s-row-head-h: 36px;    /* table header row */
    --s-control-h: 36px;     /* input, select, button */
    --s-control-h-sm: 30px;  /* in-row controls */
    --s-control-h-lg: 44px;  /* primary submit, mobile */
    --s-zebra: #f4f7fa;      /* alternating row fill */

    /* --- Elevation (only three, all restrained) ------------------------- */
    --s-e-1: 0 1px 2px rgba(19, 26, 36, 0.06);
    --s-e-2: 0 2px 8px rgba(19, 26, 36, 0.07);
    --s-e-3: 0 12px 28px rgba(19, 26, 36, 0.12);

    /* --- Motion ---------------------------------------------------------- */
    --s-dur: 160ms;
    --s-dur-lg: 240ms;
    --s-ease: cubic-bezier(0.2, 0, 0.2, 1);

    /* --- Layout ----------------------------------------------------------
       The app fills the monitor. Capping the shell left a band of unused
       space on the right while tables were cut off on the left. */
    --s-shell-max: none;
    --s-shell-max-wide: none;
    --s-shell-pad: clamp(0.875rem, 1.5vw, 1.5rem);
    --s-navbar-h: 56px;
    --s-dock-h: 64px;
    --s-sidebar-w: 272px;
    --s-sidebar-w-mini: 72px;
    --s-focus: 0 0 0 3px rgba(31, 92, 158, 0.32);

    /* --- Legacy variable remap: re-themes ~3.5k lines of old CSS -------- */
    --ink: var(--s-n900);
    --ink-2: var(--s-n800);
    --slate: var(--s-n700);
    --steel: var(--s-n100);
    --blue: var(--s-p500);
    --blue-soft: var(--s-p500);
    --gold: var(--s-brass500);
    --cloud: var(--s-n50);
    --mist: var(--s-n100);
    --border: var(--s-n200);
    --text-main: var(--s-n900);
    --text-muted: var(--s-n500);

    /* --- Bootstrap variable remap --------------------------------------- */
    --bs-body-font-family: var(--s-font-body);
    --bs-body-color: var(--s-text);
    --bs-body-bg: var(--s-canvas);
    --bs-border-color: var(--s-line);
    --bs-border-radius: var(--s-r-sm);
    --bs-border-radius-sm: 4px;
    --bs-border-radius-lg: var(--s-r-md);
    --bs-primary: var(--s-p500);
    --bs-link-color: var(--s-p600);
    --bs-link-hover-color: var(--s-p700);
    --bs-secondary-color: var(--s-text-muted);
    --bs-emphasis-color: var(--s-n900);
}

/* ==========================================================================
   2. BASE
   ========================================================================== */

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

html { -webkit-text-size-adjust: 100%; }

body {
    font-family: var(--s-font-body);
    font-size: var(--s-fs-base);
    line-height: 1.55;
    color: var(--s-text);
    /* Flat canvas — replaces the legacy triple radial-gradient wash */
    background: var(--s-canvas);
    background-image: none;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    font-variant-numeric: tabular-nums;

    /* Footer sits at the bottom on short pages instead of floating mid-screen
       above a band of empty canvas. */
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}
/* No explicit width: the flex item stretches into the space left by its own
   margins, so the sidebar offset and the centred max-width both keep working. */
body > main.page-shell { flex: 1 0 auto; }
body > footer.footer { flex: 0 0 auto; }

/* The closed drawer is parked off-canvas to the right, which widens the
   document and lets phones scroll sideways into empty space. Clipping the
   root keeps the transform animation but removes the scrollable overflow. */
html { overflow-x: clip; }
body { overflow-x: clip; }

/* Older engines without overflow: clip still need the hidden fallback. */
@supports not (overflow: clip) {
    html, body { overflow-x: hidden; }
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    font-family: var(--s-font-display);
    font-weight: 600;
    letter-spacing: -0.015em;
    color: var(--s-text);
    line-height: 1.22;
}

h1, .h1 { font-size: var(--s-fs-2xl); }
h2, .h2 { font-size: var(--s-fs-xl); }
h3, .h3 { font-size: var(--s-fs-lg); }
h4, .h4 { font-size: var(--s-fs-lg); }
h5, .h5 { font-size: var(--s-fs-md); }
h6, .h6 {
    font-size: var(--s-fs-sm);
    font-weight: 600;
    letter-spacing: 0.01em;
}

/* Los enlaces se distinguen por color y peso, nunca por subrayado: el filo
   azul basta en una interfaz densa y el hover responde oscureciendo. */
a {
    color: var(--s-p600);
    text-decoration: none;
    transition: color var(--s-dur) var(--s-ease);
}
a:hover {
    color: var(--s-p700);
    text-decoration: none;
}

small, .small { font-size: var(--s-fs-xs); line-height: 1.45; }

.text-muted,
.muted { color: var(--s-text-muted) !important; }

strong, .fw-semibold, .fw-bold { font-weight: 600; }

hr {
    border-color: var(--s-line);
    opacity: 1;
}

/* Money, weights and every other figure line up in columns. */
.s-num,
.s-money,
td, th,
input[type="number"] {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

.s-mono,
.s-folio {
    font-family: var(--s-font-mono);
    font-size: 0.9em;
    letter-spacing: -0.01em;
}

/* Universal focus treatment — never removed, always visible. */
:focus-visible {
    outline: 2px solid var(--s-p500);
    outline-offset: 2px;
    border-radius: 4px;
}

::selection {
    background: var(--s-p100);
    color: var(--s-p700);
}

/* ==========================================================================
   3. APP SHELL
   ========================================================================== */

/* Barra superior con efecto glass: translúcida y con desenfoque del contenido
   que pasa por debajo, sin perder la línea de borde que define la superficie.
   Si el navegador no soporta backdrop-filter, cae a la superficie sólida. */
.navbar {
    background: rgba(255, 255, 255, 0.78);
    -webkit-backdrop-filter: saturate(1.6) blur(14px);
    backdrop-filter: saturate(1.6) blur(14px);
    border-bottom: 1px solid var(--s-line);
    box-shadow: 0 1px 8px rgba(19, 26, 36, 0.04);
    min-height: var(--s-navbar-h);
    padding: var(--s-2) 0;
    margin-bottom: 0 !important;
    position: sticky;
    top: 0;
    z-index: 1030;
}
@supports not (backdrop-filter: blur(1px)) {
    .navbar { background: var(--s-surface); box-shadow: none; }
}

.navbar .container-fluid {
    max-width: var(--s-shell-max-wide);
    padding-inline: clamp(0.875rem, 2.5vw, 1.75rem);
}

.navbar-brand {
    font-family: var(--s-font-display);
    font-weight: 700;
    font-size: 1.0625rem;
    letter-spacing: -0.02em;
    color: var(--s-n900);
    text-transform: none;
}
/* La marca no cambia de color al pasar el cursor: es identidad, no acción. */
.navbar-brand:hover { color: var(--s-n900); text-decoration: none; }

/* La marca es la carátula de una báscula: arco grafito, aguja de latón.
   El SVG lo emite la macro brand_mark(); aquí solo viven tamaño y color. */
.brand-mark {
    width: 22px;
    height: 22px;
    margin-right: var(--s-2);
    flex: 0 0 auto;
    display: inline-block;
    vertical-align: middle;
}
.brand-mark--lg { width: 30px; height: 30px; }
.brand-mark__dial { color: var(--s-n800); }
.brand-mark__needle,
.brand-mark__hub { color: var(--s-brass500); }

/* Sobre fondos oscuros (aside del login) el dial aclara y el latón sube. */
.brand-mark--invert .brand-mark__dial { color: var(--s-n300); }
.brand-mark--invert .brand-mark__needle,
.brand-mark--invert .brand-mark__hub { color: var(--s-brass300); }

/* "Scrap" hereda el color del contenedor; "360" siempre es latón. */
.brand-word__accent { color: var(--s-brass500); }

.user-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: 0.25rem 0.625rem 0.25rem 0.25rem;
    border-radius: var(--s-r-pill);
    background: var(--s-n50);
    border: 1px solid var(--s-line);
    color: var(--s-text-soft);
    font-size: var(--s-fs-xs);
    font-weight: 500;
}

/* El chip es ahora el disparador del menú de usuario. */
.user-menu { position: relative; }
button.user-chip {
    font: inherit;
    font-size: var(--s-fs-xs);
    font-weight: 500;
    cursor: pointer;
    transition: background var(--s-dur) var(--s-ease), border-color var(--s-dur) var(--s-ease);
}
button.user-chip:hover,
button.user-chip[aria-expanded="true"] {
    background: var(--s-n100);
    border-color: var(--s-line-strong);
    color: var(--s-text);
}
.user-menu__chevron {
    width: 14px;
    height: 14px;
    color: var(--s-text-muted);
    transition: transform var(--s-dur) var(--s-ease);
}
button.user-chip[aria-expanded="true"] .user-menu__chevron { transform: rotate(180deg); }
.user-menu .s-rowmenu__menu { min-width: 12.5rem; }

.user-avatar,
.user-table-avatar,
.entity-avatar,
.entity-avatar-lg,
.s-avatar-lg {
    background: var(--s-p600);
    background-image: none;
    color: var(--s-n0);
    font-family: var(--s-font-display);
    font-weight: 600;
    border-radius: var(--s-r-pill);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    box-shadow: none;
}
.user-avatar { width: 26px; height: 26px; font-size: 0.75rem; }

.user-table-avatar { width: 32px; height: 32px; font-size: 0.8rem; flex: none; }

.s-avatar-lg {
    width: 72px;
    height: 72px;
    font-size: 1.5rem;
    flex: none;
}

/* Avatar con foto de perfil: la imagen llena el círculo sin deformarse. */
.user-avatar,
.user-table-avatar,
.s-avatar-lg {
    overflow: hidden;
}

.user-avatar > img,
.user-table-avatar > img,
.s-avatar-lg > img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* El bloque de usuario del cajón navega al perfil propio: hereda el color
   del texto para no leerse como un enlace azul. */
a.nav-drawer-user { color: var(--s-text); }
a.nav-drawer-user:hover { color: var(--s-p600); }

.menu-trigger {
    min-height: 40px;
    padding-inline: 0.75rem;
}

/* --- Page shell ------------------------------------------------------- */

.page-shell {
    max-width: none;
    margin: 0;
    padding: var(--s-5) var(--s-shell-pad) var(--s-10);
}

.page-shell-notes {
    max-width: none;
    padding-inline: var(--s-shell-pad);
}

/* Prose-like screens (login, a single form) still need a reading width. */
.s-narrow { max-width: 60rem; }

body.app-has-mobile-dock .page-shell {
    padding-bottom: calc(var(--s-dock-h) + env(safe-area-inset-bottom) + var(--s-8));
}

/* El footer también debe librar el dock: sin esto, la última línea de la
   página (versión, soporte) queda sepultada bajo la barra fija. */
@media (max-width: 767.98px) {
    body.app-has-mobile-dock footer.footer {
        padding-bottom: calc(var(--s-dock-h) + env(safe-area-inset-bottom));
    }
}

@media (min-width: 768px) {
    body.app-has-mobile-dock .page-shell { padding-bottom: var(--s-12); }
}

/* --- Drawer ----------------------------------------------------------- */

.nav-drawer {
    position: fixed;
    top: 0;
    right: 0;
    height: 100vh;
    height: 100dvh;
    z-index: 1100;
    display: flex;
    flex-direction: column;
    transform: translateX(110%);
    transition: transform var(--s-dur-lg) var(--s-ease);
    background: var(--s-surface);
    border-left: 1px solid var(--s-line);
    box-shadow: var(--s-e-3);
    width: min(320px, 88vw);
}
.nav-drawer.open { transform: translateX(0); }

body.drawer-open { overflow: hidden; }

.nav-drawer-header {
    padding: var(--s-4);
    border-bottom: 1px solid var(--s-line);
}

.nav-drawer-body {
    flex: 1;
    overflow-y: auto;
    padding: var(--s-3);
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.nav-drawer-link,
.nav-drawer-body-compact .nav-drawer-link {
    display: block;
    padding: 0.625rem 0.75rem;
    border-radius: var(--s-r-sm);
    color: var(--s-text-soft);
    background: transparent;
    background-image: none;
    border: 0;
    font-size: var(--s-fs-base);
    font-weight: 500;
    min-height: 44px;
    line-height: 1.5;
    transition: background var(--s-dur) var(--s-ease), color var(--s-dur) var(--s-ease);
}
.nav-drawer-link:hover {
    background: var(--s-n50);
    color: var(--s-text);
    text-decoration: none;
}
.nav-drawer-link.active,
.nav-drawer-body-compact .nav-drawer-link.active {
    background: var(--s-p50);
    background-image: none;
    color: var(--s-p700);
    font-weight: 600;
    box-shadow: inset 2px 0 0 var(--s-p500);
}
.nav-drawer-body-compact .nav-drawer-link::before { content: none; }

.nav-drawer-group-label {
    font-family: var(--s-font-body);
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--s-text-muted);
    padding: var(--s-4) 0.75rem var(--s-1);
}
.nav-drawer-group-label:first-child { padding-top: var(--s-1); }

/* --- Grupos colapsables ------------------------------------------------
   La etiqueta de grupo es un botón: pliega y despliega su sección. El
   estado persiste en localStorage (s360.navGroups) y el grupo que contiene
   la página activa se abre siempre. En modo mini no hay nada que plegar. */
.nav-group:first-child .nav-group__toggle { padding-top: var(--s-1); }
.nav-group__toggle {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    width: 100%;
    margin: 0;
    background: transparent;
    border: 0;
    cursor: pointer;
    text-align: left;
    transition: color var(--s-dur) var(--s-ease);
}
.nav-group__toggle:hover { color: var(--s-text-soft); }
.nav-group__chevron {
    width: 12px;
    height: 12px;
    margin-left: auto;
    flex: 0 0 auto;
    opacity: 0;
    transition: transform var(--s-dur) var(--s-ease), opacity var(--s-dur) var(--s-ease);
}
.nav-group:hover .nav-group__chevron,
.nav-group__toggle:focus-visible .nav-group__chevron,
.nav-group--closed .nav-group__chevron { opacity: 1; }
.nav-group--closed .nav-group__chevron { transform: rotate(-90deg); }

.nav-group__items {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows var(--s-dur-lg) var(--s-ease);
}
.nav-group__inner { overflow: hidden; min-height: 0; }
.nav-group--closed .nav-group__items { grid-template-rows: 0fr; }

.nav-overlay {
    position: fixed;
    inset: 0;
    background: rgba(19, 26, 36, 0.45);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--s-dur) var(--s-ease);
    z-index: 1095;
}
.nav-overlay.open { opacity: 1; pointer-events: all; }

/* --- Mobile dock ------------------------------------------------------ */

.mobile-dock { display: none; }

@media (max-width: 991.98px) {
    .mobile-dock {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1092;
        display: grid;
        padding: var(--s-1) var(--s-2) 0;
    }
    .mobile-dock--admin { grid-template-columns: repeat(5, minmax(0, 1fr)); }
    .mobile-dock--worker { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.mobile-dock {
    background: var(--s-surface);
    border-top: 1px solid var(--s-line);
    box-shadow: 0 -1px 12px rgba(19, 26, 36, 0.07);
    padding-bottom: env(safe-area-inset-bottom);
    height: calc(var(--s-dock-h) + env(safe-area-inset-bottom));
}

.mobile-dock-link {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    border: 0;
    background: transparent;
    text-decoration: none;
    font: inherit;
    border-radius: var(--s-r-sm);
    color: var(--s-text-muted);
    gap: 2px;
    min-height: 48px;
    font-weight: 500;
    transition: color var(--s-dur) var(--s-ease), background var(--s-dur) var(--s-ease);
}
.mobile-dock-link:hover,
.mobile-dock-link--menu:hover { color: var(--s-p600); background: var(--s-sunken); }
.mobile-dock-link.active { color: var(--s-p600); }
.mobile-dock-link .s-icon { width: 21px; height: 21px; }
.mobile-dock-label { font-size: 0.625rem; letter-spacing: 0.01em; }

/* --- Footer ----------------------------------------------------------- */

.footer {
    background: var(--s-surface);
    border-top: 1px solid var(--s-line) !important;
    color: var(--s-text-muted);
    font-size: var(--s-fs-xs);
}
.footer .fw-semibold { font-family: var(--s-font-display); }

.footer-brand {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    justify-content: center;
    text-align: center;
}
.footer-brand small { display: block; color: var(--s-text-muted); letter-spacing: 0.2px; }

.footer-identity { justify-content: center; text-align: left; }
.footer-wordmark {
    font-family: var(--s-font-display);
    font-weight: 600;
    font-size: var(--s-fs-base);
    letter-spacing: -0.01em;
    color: var(--s-text);
    line-height: 1.25;
}
.footer-descriptor {
    display: block;
    color: var(--s-text-muted);
    letter-spacing: 0.01em;
}

.footer-meta {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--s-2);
    text-align: center;
    line-height: 1.5;
}
.footer-meta-line {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    flex-wrap: wrap;
    justify-content: center;
}

/* La versión y el entorno son lecturas de instrumento: mono, en chip. */
.footer-chip {
    font-family: var(--s-font-mono);
    font-size: var(--s-fs-micro);
    line-height: 1;
    padding: 0.25rem 0.5rem;
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-sm);
    background: var(--s-surface-alt);
    color: var(--s-text-soft);
    letter-spacing: 0.02em;
}
.footer-chip--env {
    border-color: var(--s-warn-line);
    background: var(--s-warn-tint);
    color: var(--s-warn);
    text-transform: uppercase;
}
.footer-sep {
    width: 1px;
    height: 14px;
    background: var(--s-line);
}
.footer-link {
    color: var(--s-text-soft);
    text-decoration: none;
    font-weight: 500;
    transition: color var(--s-dur) var(--s-ease);
}
.footer-link:hover { color: var(--s-p600); }

.footer-credit { opacity: 0.75; }
.footer-credit a { color: inherit; text-decoration: none; font-weight: 500; }
.footer-credit a:hover { color: var(--s-p600); }

@media (min-width: 992px) {
    .footer-identity { justify-content: flex-start; }
    .footer-meta { align-items: flex-end; text-align: right; }
    .footer-meta-line { justify-content: flex-end; }
}

/* --- Persistent sidebar on wide screens --------------------------------
   Below 1400px the same markup stays an overlay drawer. Above it there is
   spare width, so navigation is always visible and every screen is one click
   away instead of two. */

@media (min-width: 1200px) {
    body.role-super_admin .nav-drawer,
    body.role-admin .nav-drawer,
    body.role-visor .nav-drawer {
        position: fixed;
        top: var(--s-navbar-h);
        bottom: 0;
        left: 0;
        right: auto;
        height: auto;
        /* The legacy drawer declares min-width: 320px, which would otherwise
           win over the sidebar width and push content under the panel. */
        width: var(--s-sidebar-w);
        min-width: var(--s-sidebar-w);
        max-width: var(--s-sidebar-w);
        transform: none !important;
        transition: none;
        border-left: 0;
        border-right: 1px solid var(--s-line);
        box-shadow: none;
        overflow-y: auto;
        overscroll-behavior: contain;
        z-index: 1020;
    }

    /* The drawer's own header and the burger become redundant. */
    body.role-super_admin .nav-drawer-header,
    body.role-admin .nav-drawer-header,
    body.role-visor .nav-drawer-header,
    body.role-super_admin .menu-trigger,
    body.role-admin .menu-trigger,
    body.role-visor .menu-trigger,
    body.role-super_admin .nav-overlay,
    body.role-admin .nav-overlay,
    body.role-visor .nav-overlay { display: none !important; }

    body.role-super_admin .page-shell,
    body.role-admin .page-shell,
    body.role-visor .page-shell {
        margin-left: var(--s-sidebar-w);
        max-width: none;
    }

    body.role-super_admin .footer > .container,
    body.role-admin .footer > .container,
    body.role-visor .footer > .container { padding-left: var(--s-sidebar-w); }

    /* A pinned sidebar must not lock body scroll if the drawer state lingers. */
    body.drawer-open { overflow: auto; }

    /* --- Sidebar colapsable (modo mini) --------------------------------
       Redefinir el token basta: la propia barra, el margen del contenido,
       el footer y la barra flotante del corte lo consumen. El botón vive al
       pie de la barra y el estado persiste en localStorage. */
    body.s-sidebar-mini { --s-sidebar-w: var(--s-sidebar-w-mini); }

    body.role-super_admin .nav-drawer,
    body.role-admin .nav-drawer,
    body.role-visor .nav-drawer,
    body.role-super_admin .page-shell,
    body.role-admin .page-shell,
    body.role-visor .page-shell {
        transition: width var(--s-dur) var(--s-ease),
                    min-width var(--s-dur) var(--s-ease),
                    max-width var(--s-dur) var(--s-ease),
                    margin-left var(--s-dur) var(--s-ease);
    }

    .s-sidebar-toggle {
        display: flex;
        align-items: center;
        gap: var(--s-2);
        margin: 0;
        padding: var(--s-3) var(--s-4);
        border: 0;
        border-top: 1px solid var(--s-line);
        background: transparent;
        color: var(--s-text-muted);
        font: inherit;
        font-size: var(--s-fs-sm);
        font-weight: 500;
        min-height: 44px;
        cursor: pointer;
        transition: background var(--s-dur) var(--s-ease), color var(--s-dur) var(--s-ease);
    }
    .s-sidebar-toggle:hover { background: var(--s-n50); color: var(--s-text); }
    .s-sidebar-toggle .s-icon { transition: transform var(--s-dur) var(--s-ease); }
    body.s-sidebar-mini .s-sidebar-toggle { justify-content: center; padding-inline: 0; }
    body.s-sidebar-mini .s-sidebar-toggle .s-icon { transform: rotate(180deg); }
    body.s-sidebar-mini .s-sidebar-toggle .s-sidebar-toggle__label { display: none; }

    /* En mini solo quedan los iconos, centrados; el nombre del enlace pasa al
       tooltip nativo (title) y las etiquetas de grupo se vuelven separadores. */
    body.s-sidebar-mini .nav-drawer-body { padding-inline: var(--s-2); }
    body.s-sidebar-mini .nav-drawer-link {
        display: flex;
        justify-content: center;
        padding-inline: 0;
        position: relative;
    }
    body.s-sidebar-mini .nav-drawer-link .s-grow { display: none; }
    body.s-sidebar-mini .nav-drawer-link .s-icon { width: 20px; height: 20px; }
    body.s-sidebar-mini .nav-drawer-link .s-count-badge {
        position: absolute;
        top: 4px;
        right: 8px;
        font-size: 0.625rem;
        padding: 1px 5px;
    }
    body.s-sidebar-mini .nav-drawer-group-label {
        font-size: 0;
        line-height: 0;
        padding: 0;
        margin: var(--s-2) var(--s-3);
        border-top: 1px solid var(--s-line);
    }
    body.s-sidebar-mini .nav-drawer-group-label:first-child,
    body.s-sidebar-mini .nav-group:first-child .nav-group__toggle {
        border-top: 0;
        margin: 0;
    }
    /* En mini los grupos son solo separadores: sin chevron, sin plegado. */
    body.s-sidebar-mini .nav-group__toggle { pointer-events: none; min-height: 0; }
    body.s-sidebar-mini .nav-group__chevron { display: none; }
    body.s-sidebar-mini .nav-group--closed .nav-group__items { grid-template-rows: 1fr; }
    body.s-sidebar-mini .nav-drawer-user { justify-content: center; padding-inline: 0; }
    body.s-sidebar-mini .nav-drawer-user__text { display: none; }
    body.s-sidebar-mini .nav-drawer-logout {
        display: flex;
        align-items: center;
        justify-content: center;
    }
    body.s-sidebar-mini .nav-drawer-logout__label { display: none; }
}

/* Fuera del modo barra fija, el botón de colapso no existe. */
@media (max-width: 1199.98px) {
    .s-sidebar-toggle { display: none; }
}

/* ==========================================================================
   4. PAGE HEADER
   ========================================================================== */

.page-header,
.s-page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--s-3) var(--s-4);
    margin-bottom: var(--s-5);
    padding-bottom: var(--s-4);
    border-bottom: 1px solid var(--s-line);
}

.s-page-head__main { min-width: 0; flex: 1 1 22rem; }

.s-eyebrow,
.note-page-header .text-uppercase.small,
.ops-shortcut-panel__eyebrow {
    display: block;
    font-family: var(--s-font-body);
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--s-text-muted);
    margin-bottom: var(--s-1);
}

.s-page-title {
    font-family: var(--s-font-display);
    font-size: var(--s-fs-2xl);
    font-weight: 700;
    letter-spacing: -0.02em;
    margin: 0;
    color: var(--s-n900);
}

.s-page-sub {
    margin: var(--s-1) 0 0;
    color: var(--s-text-muted);
    font-size: var(--s-fs-base);
    max-width: 68ch;
}

.s-page-head__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
}

@media (max-width: 767.98px) {
    .s-page-head,
    .page-header { align-items: stretch; }
    .s-page-head__actions { width: 100%; }
    .s-page-head__actions > .btn { flex: 1 1 auto; }
}

/* ==========================================================================
   5. BUTTONS
   --------------------------------------------------------------------------
   Hierarchy: exactly one .btn-primary per screen. .btn-outline-primary is the
   secondary action. .btn-outline-secondary is tertiary/neutral. Danger is
   reserved for destructive operations.
   ========================================================================== */

.btn {
    --bs-btn-padding-y: 0;
    --bs-btn-padding-x: 0.875rem;
    font-family: var(--s-font-body);
    font-size: var(--s-fs-base);
    font-weight: 600;
    letter-spacing: 0;
    border-radius: var(--s-r-sm);
    border-width: 1px;
    height: var(--s-control-h);
    min-height: var(--s-control-h);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4375rem;
    white-space: nowrap;
    box-shadow: none;
    transition: background-color var(--s-dur) var(--s-ease),
                border-color var(--s-dur) var(--s-ease),
                color var(--s-dur) var(--s-ease);
}
.btn:focus-visible { box-shadow: var(--s-focus); outline: none; }

.btn-sm {
    --bs-btn-padding-x: 0.625rem;
    font-size: var(--s-fs-sm);
    height: var(--s-control-h-sm);
    min-height: var(--s-control-h-sm);
}

.btn-lg {
    --bs-btn-padding-x: 1.25rem;
    font-size: var(--s-fs-base);
    height: var(--s-control-h-lg);
    min-height: var(--s-control-h-lg);
}

/* Buttons that wrap to two lines must not break the declared height. */
.btn.w-100,
.s-form-actions .btn { height: auto; min-height: var(--s-control-h); padding-block: 0.4375rem; }

/* The legacy sheet declares `background: linear-gradient(...)` on some button
   variants, which resets background-color to transparent. Setting the Bootstrap
   custom properties alone is not enough — background-color must be restated. */
.btn-primary {
    --bs-btn-bg: var(--s-p500);
    --bs-btn-border-color: var(--s-p500);
    --bs-btn-hover-bg: var(--s-p600);
    --bs-btn-hover-border-color: var(--s-p600);
    --bs-btn-active-bg: var(--s-p700);
    --bs-btn-active-border-color: var(--s-p700);
    --bs-btn-disabled-bg: var(--s-n300);
    --bs-btn-disabled-border-color: var(--s-n300);
    background-image: none;
    background-color: var(--s-p500);
    border-color: var(--s-p500);
    color: var(--s-n0);
}
.btn-primary:hover,
.btn-primary:focus-visible {
    background-color: var(--s-p600);
    border-color: var(--s-p600);
    background-image: none;
    color: var(--s-n0);
}
.btn-primary:active,
.btn-primary:disabled,
.btn-primary.disabled { background-image: none; }
.btn-primary:disabled,
.btn-primary.disabled { background-color: var(--s-n300); border-color: var(--s-n300); }

/* Outline variants must also restate their transparent fill. */
.btn-outline-primary,
.btn-outline-secondary,
.btn-outline-danger,
.btn-outline-success,
.btn-outline-dark {
    background-color: var(--s-n0);
    background-image: none;
}
.btn-outline-primary:hover  { background-color: var(--s-p50); }
.btn-outline-secondary:hover { background-color: var(--s-n50); }
.btn-outline-danger:hover   { background-color: var(--s-bad); }
.btn-outline-success:hover  { background-color: var(--s-ok); }
.btn-outline-dark:hover     { background-color: var(--s-n800); }

.btn-outline-primary {
    --bs-btn-color: var(--s-p600);
    --bs-btn-border-color: var(--s-p200);
    --bs-btn-bg: var(--s-n0);
    --bs-btn-hover-bg: var(--s-p50);
    --bs-btn-hover-border-color: var(--s-p300);
    --bs-btn-hover-color: var(--s-p700);
    --bs-btn-active-bg: var(--s-p100);
    --bs-btn-active-color: var(--s-p700);
    --bs-btn-active-border-color: var(--s-p300);
}

.btn-outline-secondary {
    --bs-btn-color: var(--s-n700);
    --bs-btn-border-color: var(--s-n200);
    --bs-btn-bg: var(--s-n0);
    --bs-btn-hover-bg: var(--s-n50);
    --bs-btn-hover-border-color: var(--s-n300);
    --bs-btn-hover-color: var(--s-n900);
    --bs-btn-active-bg: var(--s-n100);
    --bs-btn-active-color: var(--s-n900);
    --bs-btn-active-border-color: var(--s-n300);
}

.btn-outline-danger {
    --bs-btn-color: var(--s-bad);
    --bs-btn-border-color: var(--s-bad-line);
    --bs-btn-bg: var(--s-n0);
    --bs-btn-hover-bg: var(--s-bad);
    --bs-btn-hover-border-color: var(--s-bad);
    --bs-btn-hover-color: var(--s-n0);
    --bs-btn-active-bg: var(--s-bad);
    --bs-btn-active-border-color: var(--s-bad);
}

.btn-danger {
    --bs-btn-bg: var(--s-bad);
    --bs-btn-border-color: var(--s-bad);
    --bs-btn-hover-bg: #96190f;
    --bs-btn-hover-border-color: #96190f;
}

.btn-outline-success {
    --bs-btn-color: var(--s-ok);
    --bs-btn-border-color: var(--s-ok-line);
    --bs-btn-bg: var(--s-n0);
    --bs-btn-hover-bg: var(--s-ok);
    --bs-btn-hover-border-color: var(--s-ok);
    --bs-btn-hover-color: var(--s-n0);
}

.btn-success {
    --bs-btn-bg: var(--s-ok);
    --bs-btn-border-color: var(--s-ok);
    --bs-btn-hover-bg: #0d6249;
    --bs-btn-hover-border-color: #0d6249;
}

.btn-outline-dark {
    --bs-btn-color: var(--s-n800);
    --bs-btn-border-color: var(--s-n300);
    --bs-btn-hover-bg: var(--s-n800);
    --bs-btn-hover-border-color: var(--s-n800);
}

.btn-light,
.btn-export {
    background: var(--s-n0);
    background-image: none;
    border: 1px solid var(--s-n200);
    color: var(--s-n700);
    box-shadow: none;
}
.btn-light:hover,
.btn-export:hover {
    background: var(--s-n50);
    background-image: none;
    border-color: var(--s-n300);
    color: var(--s-n900);
}

.btn-link {
    color: var(--s-p600);
    font-weight: 600;
    text-decoration: none;
    min-height: 0;
    padding-inline: 0;
}
.btn-link:hover { color: var(--s-p700); text-decoration: none; }

.btn:disabled,
.btn.disabled { opacity: 0.45; cursor: not-allowed; }

/* Icon-only button: keeps a 40px hit area regardless of glyph size. */
.btn-icon {
    padding: 0;
    width: 40px;
    min-width: 40px;
}
.btn-icon.btn-sm { width: 34px; min-width: 34px; }

/* Row of actions inside a table cell */
.s-actions {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
    flex-wrap: nowrap;
    white-space: nowrap;
}

/* --- Row overflow menu --------------------------------------------------
   Two actions stay inline; the rest live behind "⋯" so the actions column
   stops pushing every list off the right edge. Built by app.js. */

.s-rowmenu { position: relative; display: inline-flex; }

.s-rowmenu__toggle {
    width: var(--s-control-h-sm);
    min-width: var(--s-control-h-sm);
    padding: 0;
    font-size: 1rem;
    line-height: 1;
}

.s-rowmenu__menu {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 40;
    min-width: 11rem;
    padding: var(--s-1);
    background: var(--s-surface);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-md);
    box-shadow: var(--s-e-3);
    display: flex;
    flex-direction: column;
    text-align: left;
}
.s-rowmenu__menu[hidden] { display: none; }

.s-rowmenu__item {
    display: block;
    padding: 0.4375rem 0.625rem;
    border: 0;
    border-radius: var(--s-r-sm);
    background: none;
    color: var(--s-n700);
    font-size: var(--s-fs-sm);
    font-weight: 500;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
    width: 100%;
}
.s-rowmenu__item:hover {
    background: var(--s-n50);
    color: var(--s-n900);
    text-decoration: none;
}
.s-rowmenu__item--danger { color: var(--s-bad); }
.s-rowmenu__item--danger:hover { background: var(--s-bad-tint); color: var(--s-bad); }

/* A POST action arrives wrapped in its own form. */
.s-rowmenu__menu form { display: block; margin: 0; }

/* Inside a horizontally scrolling table the menu would be clipped, so app.js
   switches it to fixed coordinates on open. */
.s-rowmenu__menu.is-fixed { position: fixed; top: auto; right: auto; }

/* ==========================================================================
   6. CARDS & PANELS
   --------------------------------------------------------------------------
   One surface style. A card is a border + white fill; it never nests inside
   another card. Use .s-subpanel for a grouped region inside a card.
   ========================================================================== */

.card,
.s-card {
    background: var(--s-surface);
    background-image: none;
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-md);
    box-shadow: none;
}

.card-body { padding: var(--s-5); }
@media (max-width: 575.98px) { .card-body { padding: var(--s-4); } }

.card-header {
    background: var(--s-surface);
    background-image: none;
    border-bottom: 1px solid var(--s-line);
    padding: var(--s-4) var(--s-5);
    font-weight: 600;
}

/* A card directly inside a card loses its chrome — kills the nested-box look
   that made the old UI read as unfinished. */
.card .card,
.s-card .card,
.card .s-card {
    border-radius: var(--s-r-sm);
    box-shadow: none;
    background: var(--s-surface-alt);
}

.s-card-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s-2) var(--s-4);
    margin-bottom: var(--s-4);
}

.s-card-title,
.section-title,
.note-section-title,
.note-form-card-title,
.ops-shortcut-panel__title {
    font-family: var(--s-font-display);
    font-size: var(--s-fs-lg);
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--s-n900);
    margin: 0;
}

.s-card-sub {
    color: var(--s-text-muted);
    font-size: var(--s-fs-xs);
    margin: var(--s-1) 0 0;
}

/* Grouped region inside a card: sunken fill, no border-on-border. */
.s-subpanel {
    background: var(--s-sunken);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-sm);
    padding: var(--s-4);
}

.s-section-label {
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--s-text-muted);
    margin-bottom: var(--s-2);
}

.s-divider {
    height: 1px;
    background: var(--s-line);
    border: 0;
    margin: var(--s-5) 0;
}

/* ==========================================================================
   7. STAT TILES (one implementation, replacing eight)
   ========================================================================== */

/* Los indicadores son una banda de lecturas, no tarjetas sueltas: una sola
   superficie con divisores de un pixel, como el tablero de un instrumento.
   El tono es un punto junto a la etiqueta más el color de la cifra. */
.s-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 0;
    background: var(--s-surface);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-md);
    overflow: hidden;
    margin-bottom: var(--s-5);
}
.s-stats--4 { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); }

.s-stat {
    background: var(--s-surface);
    padding: var(--s-4) var(--s-5);
    min-width: 0;
    /* Divisores: cada celda pinta su filo izquierdo y superior; los del
       borde exterior caen sobre el marco del contenedor y desaparecen. */
    box-shadow: -1px 0 0 0 var(--s-line), 0 -1px 0 0 var(--s-line);
}

.s-stat__label {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--s-text-muted);
    margin-bottom: var(--s-2);
}

.s-stat__value {
    display: block;
    font-family: var(--s-font-display);
    font-size: var(--s-fs-2xl);
    font-weight: 600;
    line-height: 1.1;
    letter-spacing: -0.02em;
    color: var(--s-n900);
    font-variant-numeric: tabular-nums;
}

.s-stat__sub {
    display: block;
    font-size: var(--s-fs-xs);
    color: var(--s-text-muted);
    margin-top: var(--s-1);
}

/* Tono: punto de 6px en la etiqueta + color de la cifra. Sin rellenos. */
.s-stat--ok .s-stat__label::before,
.s-stat--warn .s-stat__label::before,
.s-stat--bad .s-stat__label::before,
.s-stat--info .s-stat__label::before,
.s-stat--brass .s-stat__label::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: var(--s-r-pill);
    flex: 0 0 auto;
}
.s-stat--ok .s-stat__label::before    { background: var(--s-ok); }
.s-stat--ok .s-stat__value            { color: var(--s-ok); }
.s-stat--warn .s-stat__label::before  { background: var(--s-warn); }
.s-stat--warn .s-stat__value          { color: var(--s-warn); }
.s-stat--bad .s-stat__label::before   { background: var(--s-bad); }
.s-stat--bad .s-stat__value           { color: var(--s-bad); }
.s-stat--info .s-stat__label::before  { background: var(--s-p500); }
.s-stat--info .s-stat__value          { color: var(--s-p600); }
.s-stat--brass .s-stat__label::before { background: var(--s-brass300); }

/* Una lectura puede navegar a la vista que la explica: el enlace envuelve la
   celda sin alterar la banda y el hover lo insinúa con el fondo. */
.s-stats > .s-stat-link {
    display: block;
    text-decoration: none;
    color: inherit;
    min-width: 0;
}
.s-stat-link .s-stat {
    height: 100%;
    transition: background var(--s-dur) var(--s-ease);
}
.s-stat-link:hover .s-stat { background: var(--s-n25); }
.s-stat-link:hover .s-stat__label { color: var(--s-p600); }

/* Una cifra que enlaza a su desglose: tinta normal en reposo para no pintar
   la tabla de azul, y el subrayado punteado solo al pasar el cursor. */
.s-drill { color: inherit; text-decoration: none; }
.s-drill:hover { color: var(--s-p600); }
.s-drill:hover .s-zero { color: var(--s-p600); }

/* Bandas con recuento fijo de columnas: el auto-fit amontonaba lecturas en
   celdas angostas de etiqueta partida. app.js calcula el recuento balanceado
   de cada banda (data-cols); las clases --fixed-* lo declaran a mano cuando
   una pantalla quiere una composición específica (el inicio: 4 arriba, 3
   abajo). En pantallas medianas todo baja a dos columnas. */
.s-stats--fixed-4,
.s-stats[data-cols="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.s-stats--fixed-3,
.s-stats[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.s-stats[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.s-stats[data-cols="1"] { grid-template-columns: minmax(0, 1fr); }
@media (max-width: 991.98px) {
    .s-stats--fixed-4,
    .s-stats--fixed-3,
    .s-stats[data-cols="3"],
    .s-stats[data-cols="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 575.98px) {
    .s-stats,
    .s-stats[data-cols] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .s-stat { padding: var(--s-3) var(--s-4); }
    .s-stat__value { font-size: var(--s-fs-xl); }
    /* Con un número impar de lecturas, la última llena el renglón en vez de
       dejar una celda viuda con un hueco al lado. */
    .s-stats > :last-child:nth-child(odd) { grid-column: span 2; }
}

/* ==========================================================================
   8. TABLES
   --------------------------------------------------------------------------
   Data is the product. Micro-caps header, hairline rules, tabular figures,
   sticky header, sticky first column on wide tables, and a card layout on
   phones (labels injected by app.js).
   ========================================================================== */

.table,
.data-table,
.entity-table,
.report-table,
.note-table,
.notes-state-table,
.users-table,
.corte-caja-table,
.note-materials-table,
.note-payments-table,
.note-aux-table,
.note-return-table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--s-text);
    width: 100%;
    margin-bottom: 0;
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--s-fs-sm);
    color: var(--s-text);
}

/* Header: a defined 36px band, sticky, with a rule under it and light
   dividers between columns so the eye can track a value up to its label. */
.table > thead > tr > th,
.data-table thead th,
.entity-table thead th,
.report-table thead th,
.note-table thead th,
.users-table thead th,
.corte-caja-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    height: var(--s-row-head-h);
    background: var(--s-n50);
    background-image: none;
    color: var(--s-n700);
    font-family: var(--s-font-body);
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    white-space: nowrap;
    padding: 0 0.75rem;
    border-bottom: 1px solid var(--s-line-strong);
    border-right: 1px solid var(--s-n200);
    border-top: 0;
    vertical-align: middle;
}
.table > thead > tr > th:last-child { border-right: 0; }

/* Body: one declared row height, zebra striping, column rules. */
.table > tbody > tr > td,
.table > tbody > tr > th {
    height: var(--s-row-h);
    padding: 0.375rem 0.75rem;
    border-bottom: 1px solid var(--s-n100);
    border-right: 1px solid var(--s-n100);
    border-top: 0;
    vertical-align: middle;
    background: transparent;
}
.table > tbody > tr > td:last-child,
.table > tbody > tr > th:last-child { border-right: 0; }

/* Bootstrap paints cell backgrounds through --bs-table-bg on the same
   selector depth, so the stripe has to out-specify it to show at all. */
.table > tbody > tr:nth-of-type(even) > td,
.table > tbody > tr:nth-of-type(even) > th,
table.table > tbody > tr:nth-of-type(even) > * { background-color: var(--s-zebra) !important; }

.table-hover > tbody > tr:hover > *,
table.table-hover > tbody > tr:hover > * { background-color: var(--s-p50) !important; }

.table > tbody > tr:last-child > td { border-bottom: 0; }

/* .table-sm keeps the same rhythm, one step tighter. */
.table-sm > thead > tr > th { height: 32px; padding: 0 0.625rem; }
.table-sm > tbody > tr > td { height: 34px; padding: 0.25rem 0.625rem; }

/* Applied by app.js only to tables that would otherwise be cut off: tighter
   gutters buy back the width instead of hiding columns behind a scrollbar. */
@media (min-width: 768px) {
    .s-table-compact > thead > tr > th { padding-inline: 0.4375rem; letter-spacing: 0.02em; }
    .s-table-compact > tbody > tr > td { padding-inline: 0.4375rem; }
    .s-table-compact { font-size: var(--s-fs-xs); }
    .s-table-compact .badge { padding-inline: 0.375rem; letter-spacing: 0.02em; }
    .s-table-compact .btn-sm { --bs-btn-padding-x: 0.4375rem; }
}

.table-hover > tbody > tr:hover > * {
    background-color: var(--s-p50) !important;
    --bs-table-accent-bg: transparent;
    color: var(--s-text);
}

/* Rows carrying a stacked sub-line need to grow rather than clip. */
.table > tbody > tr > td:has(.s-cell-sub) { height: auto; padding-block: 0.4375rem; }

.table > tfoot > tr > td,
.table > tfoot > tr > th,
.report-table tfoot td {
    background: var(--s-n50);
    background-image: none;
    font-weight: 600;
    color: var(--s-n900);
    border-top: 1px solid var(--s-line-strong);
    padding: 0.625rem 0.75rem;
}

/* Numeric columns right-align and never wrap. */
.table .text-end,
.table th.text-end,
.table td.text-end { text-align: right; }

.table td.s-num,
.table th.s-num,
.table .s-money { text-align: right; white-space: nowrap; }

/* The primary identifier in a row */
.s-cell-key {
    font-weight: 600;
    color: var(--s-n900);
}
.s-cell-sub {
    display: block;
    font-size: var(--s-fs-xs);
    color: var(--s-text-muted);
    font-weight: 400;
}

/* --- Scroll shell ------------------------------------------------------ */

/* A table only becomes its own scroll container when it genuinely overflows
   (app.js measures and adds .is-scrollable). Otherwise the wrapper stays
   `overflow: visible` so the sticky header can pin against the page while the
   user scrolls the list — the behaviour that makes a long CRM table usable. */
.table-responsive,
.table-scroll-shell .table-responsive {
    border-radius: var(--s-r-md);
    border: 1px solid var(--s-line);
    background-color: var(--s-surface);
    overflow: visible;
}

.table-responsive.is-scrollable,
.table-scroll-shell .table-responsive.is-scrollable {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    scrollbar-color: var(--s-n300) transparent;

    /* Edge shadows that appear only on the side that still has content to
       reveal — the affordance the old build tried to give with a paragraph of
       instructions. `local` backgrounds scroll with the content, `scroll`
       backgrounds stay pinned, so the shadow fades out at each end. */
    background-image:
        linear-gradient(to right, var(--s-surface) 40%, rgba(255, 255, 255, 0)),
        linear-gradient(to left, var(--s-surface) 40%, rgba(255, 255, 255, 0)),
        linear-gradient(to right, rgba(19, 26, 36, 0.10), rgba(19, 26, 36, 0)),
        linear-gradient(to left, rgba(19, 26, 36, 0.10), rgba(19, 26, 36, 0));
    background-position: left center, right center, left center, right center;
    background-repeat: no-repeat;
    background-size: 36px 100%, 36px 100%, 12px 100%, 12px 100%;
    background-attachment: local, local, scroll, scroll;
}

/* Round the corner cells directly. Clipping the table with overflow:hidden
   would also clip the row action menu that escapes the last cell. */
.table-responsive > .table { border-radius: inherit; }
.table > thead > tr:first-child > th:first-child { border-top-left-radius: inherit; }
.table > thead > tr:first-child > th:last-child  { border-top-right-radius: inherit; }

/* The page header is sticky, so the column header parks just beneath it. */
.table > thead > tr > th { top: var(--s-navbar-h); }
.table-responsive.is-scrollable .table > thead > tr > th { top: 0; }

.table-responsive::-webkit-scrollbar,
.table-scroll-shell .table-responsive::-webkit-scrollbar { height: 10px; }
.table-responsive::-webkit-scrollbar-track { background: var(--s-n50); }
.table-responsive::-webkit-scrollbar-thumb,
.table-scroll-shell .table-responsive::-webkit-scrollbar-thumb {
    background: var(--s-n300);
    background-image: none;
    border-radius: var(--s-r-pill);
    border: 2px solid var(--s-n50);
}
.table-responsive::-webkit-scrollbar-thumb:hover,
.table-scroll-shell .table-responsive::-webkit-scrollbar-thumb:hover {
    background: var(--s-n400);
    background-image: none;
}

.table-scroll-shell {
    position: relative;
    margin-bottom: var(--s-3);
}
/* The old build faded the table edges with pseudo-elements that sat on top of
   the header row; the scrollbar and sticky column carry that signal now. */
.table-scroll-shell::before,
.table-scroll-shell::after { content: none; }

/* The "drag the table sideways" instructions are gone from the markup; the
   table layer gives affordances instead of telling you how to scroll. */

.floating-table-scrollbar { z-index: 1020; }
.floating-table-scrollbar-track::-webkit-scrollbar-thumb {
    background: var(--s-n400);
    background-image: none;
}

/* Tabla de edición de nota (note_edit): nueve columnas con controles dentro.
   Bootstrap da a los inputs de un input-group `width: 1%; min-width: 0`, así
   que al comprimirse la tabla los campos de Peso/Desc de cada subpesaje
   colapsaban a ~20 px y no se podía teclear en ellos ("no me deja editar el
   peso"), mientras el select pisaba su chevron ("encimando"). Un ancho mínimo
   real por control le da a la tabla su ancho intrínseco; el shell existente
   (`is-scrollable`) hace scroll horizontal en vez de aplastar. */
.s-table-edit .input-group { flex-wrap: nowrap; }
.s-table-edit .input-group > .form-control { min-width: 5.5rem; }
.s-table-edit .input-group > .input-group-text,
.s-table-edit .input-group > .btn { white-space: nowrap; }
.s-table-edit td > .form-select { min-width: 7.5rem; }
.s-table-edit td > .form-control { min-width: 6rem; }
.s-table-edit td > .form-text { min-width: 9rem; }

/* Sticky header for long tables */
.s-table-sticky thead th {
    position: sticky;
    top: 0;
    z-index: 2;
}

/* Sticky first column for wide tables */
.s-table-pin tbody td:first-child,
.s-table-pin thead th:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--s-surface);
    box-shadow: 1px 0 0 var(--s-n100);
}
.s-table-pin thead th:first-child { z-index: 3; background: var(--s-n50); }
.s-table-pin.table-hover tbody tr:hover td:first-child { background: var(--s-p50); }

/* Filas de detalle y filas revertidas en las tablas de nota. El desglose de
   subpesajes se hunde bajo su material; una fila revertida sigue legible pero
   ya no compite con las vivas. */
.table tr.note-subpesaje-row > td,
.table.table-hover tbody tr.note-subpesaje-row:hover > td { background: var(--s-sunken); }
.table tbody tr.is-reverted > td { color: var(--s-text-muted); }

/* ==========================================================================
   9. TABLE → CARD ON PHONES
   --------------------------------------------------------------------------
   Below 768px a .s-table-cards table restacks: each row becomes a record card
   and each cell gets its column name from the data-label attribute that
   app.js injects. No horizontal scrolling on a phone.
   ========================================================================== */

@media (max-width: 767.98px) {
    .s-table-cards > .table-responsive,
    .table-responsive.s-table-cards {
        border: 0;
        background: transparent;
        background-image: none;
        overflow: visible;
    }

    .s-table-cards table,
    .s-table-cards thead,
    .s-table-cards tbody,
    .s-table-cards tr,
    .s-table-cards td { display: block; width: 100%; }

    .s-table-cards thead { display: none; }

    /* Flex column so the naming cell can be ordered to the top of the card
       regardless of its position in the source row. */
    .s-table-cards tbody tr {
        display: flex;
        flex-direction: column;
        background: var(--s-surface);
        border: 1px solid var(--s-line);
        border-radius: var(--s-r-md);
        padding: var(--s-2) var(--s-3);
        margin-bottom: var(--s-2);
        box-shadow: var(--s-e-1);
    }
    .s-table-cards tbody td.s-cell-actions { order: 99; }

    .s-table-cards tbody td {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: var(--s-3);
        padding: 0.3125rem 0;
        border: 0;
        border-bottom: 1px dashed var(--s-n100);
        text-align: right;
        font-size: var(--s-fs-sm);
        min-height: 0;
    }

    /* --s-row-h is the height of a row in a grid. Here a cell is a stacked
       field, so it takes the height of what it holds. Left at 40px, the
       "Ver N campos más" toggle overflowed its cell and landed on top of the
       next field. The selector has to out-specify `.table > tbody > tr > td`,
       which is where the fixed height comes from. */
    .s-table-cards .table > tbody > tr > td,
    .s-table-cards .table > tbody > tr > th { height: auto; }
    .s-table-cards tbody td:last-child { border-bottom: 0; }

    .s-table-cards tbody td::before {
        content: attr(data-label);
        flex: 0 0 auto;
        max-width: 45%;
        text-align: left;
        font-size: var(--s-fs-micro);
        font-weight: 600;
        letter-spacing: 0.06em;
        text-transform: uppercase;
        color: var(--s-text-muted);
    }
    .s-table-cards tbody td[data-label=""]::before { content: none; }

    /* Empty cells collapse instead of leaving a labelled blank line. */
    .s-table-cards tbody td.s-cell-empty { display: none; }

    /* The naming column becomes the card headline: full width, no label. */
    .s-table-cards tbody td.s-cell-title {
        display: block;
        text-align: left;
        font-size: var(--s-fs-base);
        font-weight: 600;
        color: var(--s-n900);
        padding-bottom: var(--s-2);
        margin-bottom: var(--s-1);
        border-bottom: 1px solid var(--s-n100);
        order: -1;
    }
    .s-table-cards tbody td.s-cell-title::before { content: none; }

    /* Ids and counters are noise on a phone card. */
    .s-table-cards tbody td.s-cell-id { display: none; }

    /* Secondary fields of a wide record stay folded until asked for. */
    .s-table-cards tbody td.s-cell-secondary { display: none; }
    .s-table-cards tbody tr.is-expanded td.s-cell-secondary { display: flex; }

    .s-card-more {
        display: block;
        margin-top: var(--s-2);
        padding: 0;
        background: none;
        border: 0;
        color: var(--s-p600);
        font-family: var(--s-font-body);
        font-size: var(--s-fs-xs);
        font-weight: 600;
        cursor: pointer;
        min-height: 32px;
        text-align: left;
    }
    .s-card-more:hover { color: var(--s-p700); }

    /* Action cells go full width at the bottom of the card. */
    .s-table-cards tbody td.s-cell-actions {
        justify-content: flex-start;
        flex-wrap: wrap;
        gap: var(--s-2);
        padding-top: var(--s-2);
        margin-top: var(--s-1);
        border-top: 1px solid var(--s-n100);
        border-bottom: 0;
    }
    .s-table-cards tbody td.s-cell-actions::before { content: none; }
    .s-table-cards tbody td.s-cell-actions .btn { flex: 1 1 auto; }

    .s-table-cards tfoot {
        display: block;
        background: var(--s-n50);
        border: 1px solid var(--s-line);
        border-radius: var(--s-r-sm);
        padding: var(--s-2) var(--s-3);
    }
    .s-table-cards tfoot tr,
    .s-table-cards tfoot td { display: flex; justify-content: space-between; width: 100%; border: 0; }
}

/* ==========================================================================
   10. BADGES & STATUS PILLS
   --------------------------------------------------------------------------
   Status is a tinted pill with a readable foreground — never a saturated
   block of colour. Semantics: ok=green, attention=amber, problem=red,
   in-flight=blue, inert=grey.
   ========================================================================== */

.badge,
.s-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    padding: 0.1875rem 0.5rem;
    border-radius: var(--s-r-sm);
    font-family: var(--s-font-body);
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    line-height: 1.4;
    white-space: nowrap;
    border: 1px solid transparent;
    background-image: none;
}

.badge.bg-success,
.badge.bg-success-subtle,
.badge.bg-soft-success,
.s-badge--ok {
    background: var(--s-ok-tint) !important;
    border-color: var(--s-ok-line);
    color: var(--s-ok) !important;
    background-image: none !important;
}

.badge.bg-danger,
.badge.bg-danger-subtle,
.s-badge--bad {
    background: var(--s-bad-tint) !important;
    border-color: var(--s-bad-line);
    color: var(--s-bad) !important;
}

.badge.bg-warning,
.badge.bg-warning-subtle,
.s-badge--warn {
    background: var(--s-warn-tint) !important;
    border-color: var(--s-warn-line);
    color: var(--s-warn) !important;
    background-image: none !important;
}

.badge.bg-primary,
.badge.bg-info,
.badge.bg-info-subtle,
.badge.bg-primary-subtle,
.s-badge--info {
    background: var(--s-p50) !important;
    border-color: var(--s-p100);
    color: var(--s-p600) !important;
}

.badge.bg-secondary,
.badge.bg-light,
.s-badge--muted {
    background: var(--s-n50) !important;
    border-color: var(--s-n200);
    color: var(--s-n600) !important;
}

/* Bootstrap pairs .bg-warning with .text-dark; our tint sets its own colour. */
.badge.text-dark,
.badge.text-white,
.badge.text-success-emphasis,
.badge.text-primary-emphasis,
.badge.text-danger-emphasis,
.badge.text-info-emphasis { color: inherit !important; }

/* Count badge in navigation — a solid dot of attention, stays circular. */
.nav-drawer-link .badge,
.s-count-badge {
    border-radius: var(--s-r-pill);
    background: var(--s-bad) !important;
    color: var(--s-n0) !important;
    border-color: transparent;
    min-width: 20px;
    justify-content: center;
    letter-spacing: 0;
}

.chip-strong,
.entity-chip,
.placa-chip,
.user-role-chip,
.user-access-chip,
.user-status-chip,
.user-flag-chip,
.notes-op-chip,
.partner-role-badge,
.pill {
    display: inline-flex;
    align-items: center;
    gap: 0.3125rem;
    padding: 0.1875rem 0.5rem;
    border-radius: var(--s-r-sm);
    background: var(--s-n50);
    background-image: none;
    border: 1px solid var(--s-n200);
    color: var(--s-n700);
    font-size: var(--s-fs-xs);
    font-weight: 500;
    white-space: nowrap;
    box-shadow: none;
}

.status-active { color: var(--s-ok); font-weight: 600; }
.status-inactive { color: var(--s-text-muted); font-weight: 600; }

/* Semantic text colours */
.text-success { color: var(--s-ok) !important; }
.text-danger  { color: var(--s-bad) !important; }
.text-warning { color: var(--s-warn) !important; }
.text-primary { color: var(--s-p600) !important; }
.text-dark    { color: var(--s-n900) !important; }

/* A settled zero is not an error. */
.s-zero { color: var(--s-text-muted) !important; font-weight: 500; }

/* ==========================================================================
   11. FORMS
   ========================================================================== */

.form-label {
    font-size: var(--s-fs-xs);
    font-weight: 600;
    letter-spacing: 0.03em;
    color: var(--s-n700);
    margin-bottom: 0.3125rem;
}

.form-label .s-req,
.s-req {
    color: var(--s-bad);
    font-weight: 700;
    margin-left: 2px;
}

.form-control,
.form-select {
    font-family: var(--s-font-body);
    font-size: var(--s-fs-base);
    color: var(--s-text);
    background-color: var(--s-surface);
    border: 1px solid var(--s-n300);
    border-radius: var(--s-r-sm);
    /* La derecha conserva el hueco de la flecha (Bootstrap la pinta como
       fondo a 0.75rem del borde): con padding simétrico el texto de la
       opción corría debajo del chevron ("Todas las sucursal·s", "Menude·o"). */
    padding: 0 2.25rem 0 0.625rem;
    height: var(--s-control-h);
    min-height: var(--s-control-h);
    box-shadow: none;
    transition: border-color var(--s-dur) var(--s-ease), box-shadow var(--s-dur) var(--s-ease);
}

/* Below 16px iOS zooms the viewport on focus, so phones get the larger size. */
@media (max-width: 767.98px) {
    .form-control,
    .form-select,
    .form-control-sm,
    .form-select-sm {
        font-size: var(--s-fs-md);
        height: var(--s-control-h-lg);
        min-height: var(--s-control-h-lg);
    }
    textarea.form-control { height: auto; }
}

.form-control:hover:not(:disabled):not([readonly]),
.form-select:hover:not(:disabled) { border-color: var(--s-n400); }

.form-control:focus,
.form-select:focus {
    border-color: var(--s-p500);
    box-shadow: var(--s-focus);
    outline: none;
}

.form-control::placeholder { color: var(--s-n400); opacity: 1; }

.form-control:disabled,
.form-select:disabled,
.form-control[readonly] {
    background-color: var(--s-n50);
    color: var(--s-text-muted);
    border-color: var(--s-n200);
    cursor: not-allowed;
}
/* Read-only is not disabled: it still reads as real data. */
.form-control[readonly]:not(:disabled) {
    color: var(--s-n700);
    cursor: default;
}

.form-control-sm,
.form-select-sm {
    font-size: var(--s-fs-sm);
    height: var(--s-control-h-sm);
    min-height: var(--s-control-h-sm);
    padding: 0 0.5rem;
}
.form-select-sm { padding-right: 2rem; }

.form-text {
    font-size: var(--s-fs-xs);
    color: var(--s-text-muted);
    margin-top: 0.3125rem;
    line-height: 1.45;
}

textarea.form-control {
    height: auto;
    min-height: 76px;
    padding: 0.5rem 0.625rem;
    line-height: 1.5;
}

.form-check-input {
    width: 1.125rem;
    height: 1.125rem;
    margin-top: 0.1875rem;
    border-color: var(--s-n400);
    cursor: pointer;
}
.form-check-input:checked {
    background-color: var(--s-p500);
    border-color: var(--s-p500);
}
.form-check-input:focus { box-shadow: var(--s-focus); border-color: var(--s-p500); }
.form-check-label { font-size: var(--s-fs-base); cursor: pointer; }

.input-group-text {
    background: var(--s-n50);
    border-color: var(--s-n300);
    color: var(--s-text-muted);
    font-size: var(--s-fs-sm);
    font-weight: 500;
}

.is-invalid,
.form-control.is-invalid { border-color: var(--s-bad); }
.invalid-feedback { color: var(--s-bad); font-size: var(--s-fs-xs); }

/* Field grid: the one layout every form uses. */
.s-form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: var(--s-4);
}
.s-form-grid--2 { grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }
.s-field--full { grid-column: 1 / -1; }

.s-fieldset {
    border: 0;
    padding: 0;
    margin: 0 0 var(--s-6);
}
.s-fieldset:last-child { margin-bottom: 0; }
.s-fieldset > legend {
    float: none;
    width: auto;
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--s-text-muted);
    padding: 0 0 var(--s-1);
    margin-bottom: var(--s-3);
    border-bottom: 1px solid var(--s-line);
    width: 100%;
}

/* Sticky action bar at the bottom of long forms. */
.s-form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--s-2);
    padding-top: var(--s-5);
    margin-top: var(--s-5);
    border-top: 1px solid var(--s-line);
}
.s-form-actions--split { justify-content: space-between; }

@media (max-width: 575.98px) {
    .s-form-actions { flex-direction: column-reverse; align-items: stretch; }
    .s-form-actions .btn { width: 100%; }
}

/* --- Filter bar -------------------------------------------------------- */

.s-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--s-3);
    padding: var(--s-4);
    background: var(--s-surface);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-md);
    margin-bottom: var(--s-4);
}
.s-filters__field { flex: 1 1 12rem; min-width: 0; }
.s-filters__field--wide { flex: 2 1 20rem; }
.s-filters__actions { display: flex; gap: var(--s-2); flex: 0 0 auto; }

/* On phones the filter block collapses behind a summary so the data is the
   first thing on screen, not twelve inputs. */
.s-filters-collapse > summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
    padding: 0.625rem var(--s-4);
    background: var(--s-surface);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-md);
    font-size: var(--s-fs-base);
    font-weight: 600;
    color: var(--s-n700);
    min-height: 44px;
    margin-bottom: var(--s-3);
}
.s-filters-collapse > summary::-webkit-details-marker { display: none; }
.s-filters-collapse > summary::after {
    content: "";
    width: 8px;
    height: 8px;
    border-right: 2px solid var(--s-n500);
    border-bottom: 2px solid var(--s-n500);
    transform: rotate(45deg) translateY(-2px);
    transition: transform var(--s-dur) var(--s-ease);
    flex: 0 0 auto;
}
.s-filters-collapse[open] > summary::after { transform: rotate(-135deg) translateY(-2px); }
.s-filters-collapse[open] > summary { margin-bottom: var(--s-2); }

@media (min-width: 768px) {
    .s-filters-collapse > summary { display: none; }
    .s-filters-collapse > .s-filters { display: flex; }
}

/* --- Segmented control (replaces the loose pill rows) ------------------ */

.s-segmented,
.ops-inline-toolbar,
.note-state-tabs {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 2px;
    padding: 3px;
    background: var(--s-n50);
    background-image: none;
    background-color: var(--s-n50);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-sm);
    max-width: 100%;
}

/* On a phone the strip scrolls instead of hiding half its items. */
@media (max-width: 767.98px) {
    .s-segmented,
    .ops-inline-toolbar,
    .note-state-tabs {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
    }
    .s-segmented::-webkit-scrollbar,
    .ops-inline-toolbar::-webkit-scrollbar,
    .note-state-tabs::-webkit-scrollbar { display: none; }
    .s-segmented > *,
    .ops-inline-toolbar > *,
    .note-state-tabs > * { flex: 0 0 auto; }
}

.s-segmented__item,
.ops-inline-pill,
.note-state-tab {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.375rem 0.6875rem;
    border-radius: 4px;
    background: transparent;
    background-image: none;
    border: 0;
    color: var(--s-text-soft);
    font-size: var(--s-fs-sm);
    font-weight: 600;
    white-space: nowrap;
    min-height: 34px;
    text-decoration: none;
    transition: background var(--s-dur) var(--s-ease), color var(--s-dur) var(--s-ease);
}
.s-segmented__item:hover,
.ops-inline-pill:hover,
.note-state-tab:hover {
    background: var(--s-n100);
    color: var(--s-n900);
    text-decoration: none;
}
.s-segmented__item.active,
.ops-inline-pill.active,
.note-state-tab.active {
    background: var(--s-surface);
    background-image: none;
    color: var(--s-p700);
    box-shadow: var(--s-e-1);
    border: 1px solid var(--s-line);
}

.s-segmented__count {
    font-size: var(--s-fs-micro);
    font-weight: 700;
    padding: 0 0.3125rem;
    border-radius: var(--s-r-pill);
    background: var(--s-n200);
    color: var(--s-n700);
    min-width: 1.125rem;
    text-align: center;
}
.s-segmented__item.active .s-segmented__count { background: var(--s-p100); color: var(--s-p700); }

/* --- In-page section index ---------------------------------------------
   Honest anchor links to the sections of a long record page. These are not
   tabs: everything is on the page, and the index says so. */

.s-anchor-nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    padding: var(--s-3) 0 var(--s-4);
    margin-bottom: var(--s-4);
    border-bottom: 1px solid var(--s-line);
}

@media (max-width: 767.98px) {
    .s-anchor-nav {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        margin-inline: calc(var(--s-4) * -1);
        padding-inline: var(--s-4);
    }
    .s-anchor-nav::-webkit-scrollbar { display: none; }
    .s-anchor-nav > * { flex: 0 0 auto; }
}

/* Anchored sections must not land under the sticky navbar. */
[id] { scroll-margin-top: calc(var(--s-navbar-h) + var(--s-4)); }

/* --- Definition rows: a read-only value is not an input ----------------- */

.s-defs {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    gap: var(--s-3) var(--s-5);
}

.s-def__label {
    display: block;
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--s-text-muted);
    margin-bottom: 2px;
}

.s-def__value {
    display: block;
    font-size: var(--s-fs-base);
    font-weight: 500;
    color: var(--s-n900);
    /* Emails and account numbers have no spaces to break on. */
    overflow-wrap: anywhere;
}
.s-def__value:empty::before { content: "—"; color: var(--s-text-muted); }

/* A second line qualifying the value: where a figure came from, what it is
   internal to. Replaces .note-meta-sub. */
.s-def__sub {
    display: block;
    font-size: var(--s-fs-xs);
    color: var(--s-text-muted);
    margin-top: 2px;
}

/* The grid item itself. The definition list replaced a grid of bordered
   boxes: a box reads as a control the user can act on, and these are values
   you can only look at. Borders here would also nest inside the card that
   already draws one. */
.s-def { min-width: 0; }

/* ==========================================================================
   12. ALERTS & EMPTY STATES
   ========================================================================== */

.alert {
    border-radius: var(--s-r-sm);
    border: 1px solid transparent;
    border-left-width: 3px;
    padding: var(--s-3) var(--s-4);
    font-size: var(--s-fs-base);
    background-image: none;
}
.alert-success { background: var(--s-ok-tint); border-color: var(--s-ok-line); border-left-color: var(--s-ok); color: #0b5c45; }
.alert-danger  { background: var(--s-bad-tint); border-color: var(--s-bad-line); border-left-color: var(--s-bad); color: #8c1a12; }
.alert-warning { background: var(--s-warn-tint); border-color: var(--s-warn-line); border-left-color: var(--s-warn); color: #8a4008; }
.alert-info,
.alert-primary { background: var(--s-p50); border-color: var(--s-p100); border-left-color: var(--s-p500); color: var(--s-p700); }
.alert-secondary,
.alert-light { background: var(--s-n50); border-color: var(--s-n200); border-left-color: var(--s-n400); color: var(--s-n700); }

.s-empty,
.notes-empty-state,
.evidence-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: var(--s-2);
    padding: var(--s-8) var(--s-4);
    background: var(--s-n25);
    border: 1px dashed var(--s-n300);
    border-radius: var(--s-r-md);
    color: var(--s-text-muted);
    min-height: 0;
}
.s-empty__title {
    font-family: var(--s-font-display);
    font-size: var(--s-fs-md);
    font-weight: 600;
    color: var(--s-n700);
}
.s-empty__text { font-size: var(--s-fs-sm); max-width: 44ch; margin: 0; }
.s-empty .s-icon { color: var(--s-n400); width: 28px; height: 28px; }

/* The toggle only exists in card mode. */
@media (min-width: 768px) {
    .s-card-more { display: none; }
}

/* ==========================================================================
   13. ICONS
   --------------------------------------------------------------------------
   Currentcolor stroke icons from a single sprite. Emoji are never icons.
   ========================================================================== */

.s-icon {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
    stroke: currentColor;
    stroke-width: 1.75;
    stroke-linecap: round;
    stroke-linejoin: round;
    fill: none;
    vertical-align: -0.18em;
}
.s-icon--sm { width: 15px; height: 15px; }
.s-icon--lg { width: 22px; height: 22px; }

.s-icon-tile {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: var(--s-r-sm);
    background: var(--s-p50);
    background-image: none;
    color: var(--s-p600);
    border: 1px solid var(--s-p100);
    flex: 0 0 auto;
}

/* ==========================================================================
   14. NAVIGATION TILES (dashboard)
   ========================================================================== */

.s-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: var(--s-3);
}

.s-tile,
.ops-shortcut-card,
.action-card {
    display: flex;
    align-items: flex-start;
    gap: var(--s-3);
    min-height: 0;
    padding: var(--s-4);
    border-radius: var(--s-r-md);
    border: 1px solid var(--s-line);
    background: var(--s-surface);
    background-image: none;
    color: var(--s-text);
    text-decoration: none;
    box-shadow: none;
    transition: border-color var(--s-dur) var(--s-ease), background var(--s-dur) var(--s-ease);
}
.s-tile:hover,
.ops-shortcut-card:hover,
.action-card:hover {
    border-color: var(--s-p300);
    background: var(--s-p50);
    color: var(--s-text);
    text-decoration: none;
    transform: none;
    box-shadow: none;
}
.ops-shortcut-card::before,
.ops-shortcut-card::after,
.action-card::before,
.action-card::after { content: none; }

.s-tile__body { min-width: 0; }
.s-tile__title {
    display: block;
    font-family: var(--s-font-display);
    font-size: var(--s-fs-md);
    font-weight: 600;
    color: var(--s-n900);
    margin-bottom: 2px;
}
.s-tile__text {
    display: block;
    font-size: var(--s-fs-xs);
    color: var(--s-text-muted);
    line-height: 1.45;
}

/* ==========================================================================
   15. OVERRIDES DE BOOTSTRAP Y GANCHOS ACTIVOS
   --------------------------------------------------------------------------
   Lo que queda del antiguo remap legado: utilidades de Bootstrap ancladas a
   los tokens y los pocos ganchos de comportamiento que conservan estilo aquí.
   ========================================================================== */

.bg-light { background-color: var(--s-n50) !important; }
.border { border-color: var(--s-line) !important; }

/* Ganchos del formulario de nota del trabajador y de la página de evidencias */
.evid-label {
    display: block;
    font-family: var(--s-font-body);
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--s-text-muted);
    margin-bottom: var(--s-1);
}
.subpesaje-actions .btn { min-height: 36px; }
.evid-thumb { border-radius: var(--s-r-sm); background: var(--s-n100); }

/* ==========================================================================
   16. WORKER NOTE FORM — the one screen used on a phone all day
   ========================================================================== */

.note-step-header,
.s-step-head {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    margin-bottom: var(--s-3);
    padding-bottom: var(--s-3);
    border-bottom: 1px solid var(--s-line);
}

.s-step-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: var(--s-r-pill);
    background: var(--s-p500);
    color: var(--s-n0);
    font-family: var(--s-font-display);
    font-size: var(--s-fs-sm);
    font-weight: 700;
    flex: 0 0 auto;
}

.note-step-title,
.s-step-title {
    font-family: var(--s-font-display);
    font-size: var(--s-fs-lg);
    font-weight: 600;
    margin: 0;
    color: var(--s-n900);
}

/* A weighing row is a horizontal instrument reading, not six stacked cards. */
.subpesaje-group {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
    align-items: end;
    gap: var(--s-2);
    padding: var(--s-3);
    background: var(--s-surface);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-sm);
    margin-bottom: var(--s-2);
}
.subpesaje-group .form-label { font-size: var(--s-fs-micro); margin-bottom: 2px; }
/* The legacy sheet gave these a 140px floor for a flex row; in a grid that
   floor overflows the columns on a phone. */
.subpesaje-group .form-control { min-width: 0; }
.subpesaje-neto { background: var(--s-sunken); font-weight: 600; }
.subpesaje-actions {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    flex-wrap: wrap;
}

/* Photo control + its status read as one unit, kept clear of the destructive
   button by the space-between above. */
.subpesaje-actions__photo {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    flex-wrap: wrap;
}

/* The legacy sheet stretched every control inside a weighing row to full
   width, which turned the actions row into a three-deep stack on a phone. */
.subpesaje-group .subpesaje-actions .btn,
.subpesaje-group .subpesaje-actions .evid-label {
    width: auto;
    min-width: 0;
}
.subpesaje-actions .evid-label {
    display: inline;
    margin-bottom: 0;
    border: 0;
    background: none;
    letter-spacing: 0;
    text-transform: none;
    font-size: var(--s-fs-sm);
    color: inherit;
}
.subpesaje-actions .evid-label.is-ready { color: var(--s-ok); }
.subpesaje-actions .evid-label.is-error { color: var(--s-bad); }
.subpesaje-actions .evid-label.is-uploading { color: var(--s-p600); }
.subpesaje-actions .btn { min-height: 40px; }
.subpesaje-actions .evid-thumb { width: 34px; height: 34px; }

@media (max-width: 380px) {
    .subpesaje-group { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ==========================================================================
   17. LOGIN
   ========================================================================== */

.s-auth {
    min-height: calc(100dvh - var(--s-navbar-h));
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--s-6) var(--s-4);
}

.s-auth__card {
    width: min(420px, 100%);
    background: var(--s-surface);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-lg);
    box-shadow: var(--s-e-2);
    padding: var(--s-8);
}

.s-auth__brand {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    margin-bottom: var(--s-6);
}
.s-auth__wordmark {
    font-family: var(--s-font-display);
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.02em;
}

/* --- Login: panel dividido ---------------------------------------------
   El formulario va primero en el DOM (lectores de pantalla y foco); el
   lienzo de marca se dibuja a la izquierda solo cuando hay ancho para él. */
.s-auth__panel {
    width: min(920px, 100%);
    display: grid;
    grid-template-columns: 1fr;
    background: var(--s-surface);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-lg);
    box-shadow: var(--s-e-3);
    overflow: hidden;
}
.s-auth__main {
    padding: var(--s-10) var(--s-8);
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.s-auth__aside { display: none; }

@media (min-width: 992px) {
    .s-auth__panel { grid-template-columns: 11fr 9fr; min-height: 560px; }
    .s-auth__main { padding: var(--s-12) var(--s-10); }
    /* En pantalla dividida la marca ya vive en el lienzo izquierdo. */
    .s-auth__main .s-auth__brand { display: none; }
    .s-auth__aside {
        order: -1;
        display: flex;
        flex-direction: column;
        position: relative;
        overflow: hidden;
        background: var(--s-n900);
        background-image: repeating-linear-gradient(
            135deg,
            transparent 0 12px,
            color-mix(in srgb, var(--s-n0) 2%, transparent) 12px 13px
        );
        color: var(--s-n0);
        padding: var(--s-8) var(--s-10) var(--s-8);
    }
}

.s-auth__aside-brand {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    position: relative;
    z-index: 1;
}
.s-auth__aside-brand .s-auth__wordmark { color: var(--s-n0); }
.s-auth__aside-brand .brand-word__accent { color: var(--s-brass300); }

.s-auth__aside-body {
    position: relative;
    z-index: 1;
    margin-top: auto;
    padding-top: var(--s-12);
}
.s-auth__eyebrow {
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--s-brass300);
    margin: 0 0 var(--s-3);
}
.s-auth__headline {
    font-family: var(--s-font-display);
    font-size: 1.625rem;
    font-weight: 600;
    line-height: 1.25;
    letter-spacing: -0.01em;
    color: var(--s-n0);
    margin: 0 0 var(--s-3);
    max-width: 21ch;
}
.s-auth__lede {
    font-size: var(--s-fs-base);
    line-height: 1.6;
    color: var(--s-n400);
    margin: 0;
    max-width: 36ch;
}
.s-auth__aside-meta {
    position: relative;
    z-index: 1;
    margin: var(--s-8) 0 0;
    font-family: var(--s-font-mono);
    font-size: var(--s-fs-micro);
    letter-spacing: 0.04em;
    color: var(--s-n500);
}

/* Dibujo técnico de la carátula: decorativo, recortado en la esquina. */
.s-auth__gauge {
    position: absolute;
    top: -72px;
    right: -72px;
    width: 300px;
    height: 300px;
    color: var(--s-n700);
    opacity: 0.9;
}
.s-auth__gauge-brass { color: var(--s-brass300); }

@media (prefers-reduced-motion: no-preference) {
    .s-auth__panel {
        animation: s-auth-in var(--s-dur-lg) var(--s-ease) both;
    }
    @keyframes s-auth-in {
        from { opacity: 0; transform: translateY(8px); }
        to   { opacity: 1; transform: none; }
    }
}

/* Password field with a reveal control that keeps a 44px hit area. */
.s-password { position: relative; }
.s-password .form-control { padding-right: 2.875rem; }
.s-password__toggle {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 0;
    color: var(--s-n500);
    cursor: pointer;
    border-radius: 0 var(--s-r-sm) var(--s-r-sm) 0;
}
.s-password__toggle:hover { color: var(--s-n800); }
.s-password__toggle[aria-pressed="true"] { color: var(--s-p600); }

@media (max-width: 575.98px) {
    .s-auth { align-items: flex-start; padding-top: var(--s-8); }
    .s-auth__card { padding: var(--s-6) var(--s-5); border-radius: var(--s-r-md); }
    .s-auth__main { padding: var(--s-6) var(--s-5); }
    .s-auth__panel { border-radius: var(--s-r-md); }
}

/* ==========================================================================
   18. UTILITIES
   ========================================================================== */

.s-stack   { display: flex; flex-direction: column; gap: var(--s-4); }
.s-stack-2 { display: flex; flex-direction: column; gap: var(--s-2); }

/* Ancho de lectura corto para flujos secuenciales (cambio de contraseña). */
.s-measure-sm { max-width: 24rem; }
.s-row     { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); }
.s-between { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-3); }
.s-grow    { flex: 1 1 auto; min-width: 0; }
.s-truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.s-scroll-x {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
}

/* Horizontal chip rail — used for filter pills on phones. */
@media (max-width: 767.98px) {
    .s-rail {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        gap: var(--s-2);
        padding-bottom: var(--s-2);
        margin-inline: calc(var(--s-4) * -1);
        padding-inline: var(--s-4);
        scrollbar-width: none;
    }
    .s-rail::-webkit-scrollbar { display: none; }
    .s-rail > * { flex: 0 0 auto; }
}

/* ==========================================================================
   19. RESPONSIVE CORRECTIONS
   ========================================================================== */

@media (max-width: 991.98px) {
    :root { --s-fs-2xl: 1.5rem; }
}

@media (max-width: 767.98px) {
    .page-shell,
    .page-shell-notes { padding-top: var(--s-4); }

    /* Nothing may push the viewport sideways on a phone. */
    body { overflow-x: hidden; }

    .s-page-head { margin-bottom: var(--s-4); }

    .row > [class*="col-"] { min-width: 0; }

    /* Two-column workspaces that were never given phone behaviour. The worst
       offender declared a 320px minimum sidebar, which left the main column a
       few pixels wide and rendered its text one character per line. */
    .note-workspace-grid,
    .note-payment-panels,
    .note-comments-grid,
    .note-adjust-layout,
    .note-adjust-form-grid,
    .entity-form-grid,
    .user-form-grid,
    .notes-toolbar-grid,
    .notes-attention-grid,
    .cash-count-sheet,
    .s-form-grid,
    .s-form-grid--2,
    .s-defs { grid-template-columns: 1fr !important; }

    /* Fixed three-column strips become two so figures stay legible. */
    .note-adjust-preview-grid,
    .note-edit-impact-grid,
    .note-return-summary-grid,
    .evidence-meta-grid,
    .notes-deadline-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }

    /* Any remaining grid must not demand more width than the screen has. */
    [class*="-grid"] > * { min-width: 0; }

    .s-filters { padding: var(--s-3); gap: var(--s-2); }
    .s-filters__field,
    .s-filters__field--wide { flex: 1 1 100%; }
    .s-filters__actions { width: 100%; }
    .s-filters__actions .btn { flex: 1 1 auto; }
}

@media (max-width: 575.98px) {
    .s-page-title { font-size: 1.375rem; }
    .card-body { padding: var(--s-4); }
    .s-auth__card,
    .s-auth__panel { box-shadow: none; }
}

/* ==========================================================================
   20. ACCESSIBILITY & PRINT
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

.s-visually-hidden,
.visually-hidden-focusable:not(:focus):not(:focus-within) {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.s-skip-link {
    position: absolute;
    left: var(--s-4);
    top: -60px;
    z-index: 2000;
    background: var(--s-p600);
    color: var(--s-n0);
    padding: 0.625rem var(--s-4);
    border-radius: var(--s-r-sm);
    font-weight: 600;
    transition: top var(--s-dur) var(--s-ease);
}
.s-skip-link:focus { top: var(--s-2); color: var(--s-n0); text-decoration: none; }

@media print {
    .navbar,
    .nav-drawer,
    .nav-overlay,
    .mobile-dock,
    .footer,
    .floating-table-scrollbar,
    .s-page-head__actions,
    .s-filters,
    .s-filters-collapse,
    .btn { display: none !important; }

    body { background: #fff; font-size: 11pt; }
    .page-shell { max-width: none; padding: 0; }
    .card, .s-card, .table-responsive { border: 1px solid #999; break-inside: avoid; }
    .table { font-size: 9pt; }
    .table > thead > tr > th { background: #eee !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    a { color: inherit; text-decoration: none; }
}

/* Two badges that tell one status story sit on one line, wrapping only when
   the column is genuinely too narrow. */
.s-badge-stack {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem;
}

/* --- Column sizing ------------------------------------------------------
   A table lays out on content, so a column of icon buttons will happily
   absorb slack it has no use for. `width: 1%` is the table idiom for
   "shrink to fit", handing that width back to the columns carrying data. */
.table th.s-col-fit,
.table td.s-cell-actions,
.table td.s-cell-id {
    width: 1%;
    white-space: nowrap;
}

/* A free-text column — a description, a comment — must be allowed to wrap,
   including inside a long unbroken string. This replaces .corte-caja-text-cell,
   which shipped alongside min-width forcers that stopped the table fitting. */
.table td.s-cell-wrap,
.table th.s-cell-wrap {
    white-space: normal;
    word-break: break-word;
    overflow-wrap: anywhere;
    line-height: 1.4;
}

/* Numeric columns never wrap, but they are not fit-to-content: making every
   column request the same tiny width makes the browser share the slack out
   between them, which is how the action column ended up 180px wide. */
.table th.text-end,
.table td.text-end { white-space: nowrap; }

/* Width-saving measures for a cramped desktop table. They must not reach the
   phone layout, where each row is already a card and stacking these elements
   pushed badges out of their cell. */
@media (min-width: 768px) {
    .s-table-compact .s-badge-stack { flex-direction: column; align-items: flex-start; }
    .s-table-compact td .badge + .badge { margin-top: 2px; }
    .s-table-compact td .s-cell-key { display: block; }
    .s-table-compact td .s-cell-key + .badge { margin-top: 2px; }
}

/* ==========================================================================
   21. CATEGORÍAS, COLAPSABLES Y BARRA DE RESUMEN
   --------------------------------------------------------------------------
   Componentes que nacieron en el corte de caja y se promovieron a vocabulario
   canónico: filas categorizadas por método de pago (.s-cat-row-*), la leyenda
   de colores (.s-cat-legend), la píldora de referencia de pago
   (.s-method-pill), secciones colapsables (.s-collapse) y la barra flotante
   de resumen (.s-dockbar). Cualquier pantalla puede usarlos.
   ========================================================================== */

/* --- Account colour coding ------------------------------------------------
   A movement belongs to one account and the operator scans for "which of
   these is mine". That is categorical, so it gets a categorical hue — but as
   a 3px spine, the same device .s-stat uses, not a wash over the whole row.
   The wash fought the zebra striping (it needed !important to win), flattened
   hover, and left seven pastel bands where a ledger should be. */

:root {
    --s-cat-cash:     var(--s-ok);
    --s-cat-1:        #0b6bcb;
    --s-cat-2:        #b45309;
    --s-cat-3:        #b42318;
    --s-cat-4:        #5b53b8;
    --s-cat-5:        #0e7490;
    --s-cat-transfer: var(--s-n500);
}

.s-cat-row-cash      { --s-cat: var(--s-cat-cash); }
.s-cat-row-account-1 { --s-cat: var(--s-cat-1); }
.s-cat-row-account-2 { --s-cat: var(--s-cat-2); }
.s-cat-row-account-3 { --s-cat: var(--s-cat-3); }
.s-cat-row-account-4 { --s-cat: var(--s-cat-4); }
.s-cat-row-account-5 { --s-cat: var(--s-cat-5); }
.s-cat-row-transfer  { --s-cat: var(--s-cat-transfer); }

.table tr[class*="s-cat-row-"] td:first-child {
    box-shadow: inset 3px 0 0 var(--s-cat);
}

/* In card mode each row is already a card, so the spine is its left edge. */
@media (max-width: 767.98px) {
    .table.s-table-cards tr[class*="s-cat-row-"] td:first-child { box-shadow: none; }
    .table.s-table-cards tr[class*="s-cat-row-"] { border-left: 3px solid var(--s-cat); }
}

/* --- Legend ---------------------------------------------------------------
   Colour is never the only signal: the legend names every account. The hue
   rides a dot so the label stays readable text instead of tinted-on-tinted. */

.s-cat-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2) var(--s-4);
    margin-bottom: var(--s-3);
}

.s-cat-legend__item {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    font-size: var(--s-fs-xs);
    font-weight: 500;
    color: var(--s-text-soft);
}
.s-cat-legend__item::before {
    content: "";
    width: 10px;
    height: 10px;
    border-radius: 2px;
    background: var(--s-cat, var(--s-n400));
    flex: 0 0 auto;
}

/* --- Payment state on a movement row -------------------------------------
   Was a 999px pill in its own colours. It is a status, so it reads as one. */

.s-method-pill {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
    min-height: var(--s-control-h-sm);
    padding: 0 var(--s-2);
    border-radius: var(--s-r-sm);
    border: 1px solid transparent;
    font-size: var(--s-fs-xs);
    font-weight: 500;
    line-height: 1.4;
    white-space: nowrap;
    cursor: default;
}
button.s-method-pill { cursor: pointer; }
button.s-method-pill:hover { filter: brightness(0.97); }
.s-method-pill--pending {
    background: var(--s-warn-tint);
    border-color: var(--s-warn-line);
    color: var(--s-warn);
}
.s-method-pill--ok {
    background: var(--s-n50);
    border-color: var(--s-n200);
    color: var(--s-p700);
}

/* --- Section scaffolding --------------------------------------------------
   The private sticky nav and section headers are gone: this screen uses
   .s-anchor-nav and .s-card-title like every other one. The collapse stays,
   because the sections really do fold. */

.s-anchor-target { scroll-margin-top: calc(var(--s-navbar-h) + var(--s-4)); }

.s-collapse { margin-bottom: var(--s-2); }

.s-collapse__trigger {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    width: 100%;
    padding: var(--s-2) 0;
    background: none;
    border: none;
    border-bottom: 1px solid var(--s-line);
    cursor: pointer;
    text-align: left;
    transition: border-color var(--s-dur) var(--s-ease);
}
.s-collapse__trigger:hover { border-bottom-color: var(--s-line-strong); }
/* Cuando el disparador comparte renglon flex con otros controles (home:
   "Actividad reciente" + selector de sucursal + "Ver todas"), el width: 100%
   expulsaba a los hermanos a una segunda linea encimada bajo el subtitulo.
   Con s-grow el boton absorbe el espacio SOBRANTE: con base auto reclamaba el
   ancho de su subtitulo (877 px) y aplastaba al select por debajo de su
   propio texto; con base 0 en telefono "cabia" todo en un renglon y el
   titulo se quedaba con 26 px. Base de 16rem: en telefono los controles
   bajan a la segunda linea y el titulo ocupa la primera; en escritorio
   crece hasta llenar. */
.s-collapse__trigger.s-grow { width: auto; flex: 1 1 16rem; min-width: 0; }
.s-collapse__body { padding: var(--s-4) 0 var(--s-2); }

.s-collapse__chevron {
    margin-left: auto;
    color: var(--s-text-muted);
    transition: transform var(--s-dur) var(--s-ease);
}
.s-collapse__trigger[aria-expanded="true"] .s-collapse__chevron { transform: rotate(180deg); }

/* --- Running totals bar ---------------------------------------------------
   A dark slab pinned over a light app read as a different product. It
   summarises the screen you are on, so it is a surface with a top border —
   and it still clears the phone dock. */

.s-dockbar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    gap: var(--s-4);
    padding: var(--s-2) var(--s-5);
    background: var(--s-surface);
    border-top: 1px solid var(--s-line-strong);
    box-shadow: var(--s-e-2);
    color: var(--s-text);
    font-size: var(--s-fs-xs);
    z-index: 150;
}
.app-has-mobile-dock .s-dockbar { bottom: 56px; }

/* The bar is fixed to the viewport, so left: 0 put its first figure under the
   pinned sidebar — "Sobrante" read as "brante". It starts where the content
   starts, like everything else on the page. */
@media (min-width: 1200px) {
    body.role-super_admin .s-dockbar,
    body.role-admin .s-dockbar,
    body.role-visor .s-dockbar {
        left: var(--s-sidebar-w);
    }
}

.s-dockbar__item { display: flex; flex-direction: column; }
.s-dockbar__item span {
    font-size: var(--s-fs-micro);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--s-text-muted);
}
.s-dockbar__item strong { font-size: var(--s-fs-base); font-weight: 600; }
.s-dockbar__divider {
    width: 1px;
    align-self: stretch;
    background: var(--s-line);
    flex-shrink: 0;
}
.s-dockbar__action { margin-left: auto; }

@media (max-width: 767.98px) {
    .s-dockbar { gap: var(--s-3); padding: var(--s-2) var(--s-4); overflow-x: auto; }
    .s-dockbar__divider { display: none; }
}

/* --- Sticky side rail ----------------------------------------------------
   The panel that has to stay in reach while you scroll a long record: the
   approval controls of a nota, the payment reference. Replaces
   .note-side-card, whose offset was a hard-coded 88px. It only sticks where
   there is room to scroll past it. */
.s-sticky-rail {
    position: sticky;
    top: calc(var(--s-navbar-h) + var(--s-3));
}

@media (max-width: 1023.98px) {
    /* On a narrow screen the rail is stacked under the content it belongs to,
       so sticking it would park it over the next section. */
    .s-sticky-rail { position: static; }
}

/* --- Denomination count input --------------------------------------------
   Was style="max-width: 7rem" repeated on six table rows. */
.s-input-qty { max-width: 7rem; }

/* --- Arqueo de denominaciones en el teléfono ----------------------------
   Contar efectivo con el teléfono en la mano es EL caso de uso del corte.
   La tabla se excluye del restack (data-table-mode="grid") y conserva sus
   tres columnas — una fila por denominación —, con el campo de cantidad a
   altura de pulgar. */
@media (max-width: 767.98px) {
    .s-denom-table { table-layout: fixed; }
    .s-denom-table th:first-child,
    .s-denom-table td:first-child { width: 34%; }
    .s-denom-table th,
    .s-denom-table td {
        padding-inline: var(--s-2);
        vertical-align: middle;
    }
    .s-denom-table .s-input-qty {
        max-width: none;
        width: 100%;
        min-height: var(--s-control-h-lg);
        font-size: var(--s-fs-md);
    }
}

/* ==========================================================================
   22. MEDIA: EVIDENCE CARDS, THUMBNAILS AND THE PICKER SHEET
   --------------------------------------------------------------------------
   Photographic evidence of a weighing is the one place in this app where the
   record is an image, so the grid of cards, the thumbnail strip and the
   camera/gallery sheet are real components. They lived in style.css under
   evidence-* names, which meant the worker's capture screen and the admin's
   evidence screen each depended on the sheet that is being retired. They are
   the same component, so they get one name and one set of tokens.
   ========================================================================== */

/* --- Two fields that are one decision -------------------------------------
   Kilos and their price per kilo belong together: in separate columns the
   returns table did not fit its column. The label rides beside the control
   instead of above it, so the pair stays one row tall. */

.s-stack-tight {
    display: flex;
    flex-direction: column;
    gap: var(--s-1);
    align-items: flex-end;
}

.s-field-inline {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
}
.s-field-inline .s-def__label { margin-bottom: 0; }

/* --- Chip list ------------------------------------------------------------
   A wrapped row of small facts that belong to one record: the subpesajes of
   a material, each with its own link to its evidence. Replaces
   note-subpesajes / note-subpesaje-item / note-subpesaje-link. */

.s-chip-list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2) var(--s-3);
}

.s-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    font-size: var(--s-fs-xs);
}

.s-chip__link {
    padding: 2px var(--s-2);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-sm);
    color: var(--s-text-muted);
    font-size: var(--s-fs-micro);
    text-decoration: none;
    white-space: nowrap;
}
.s-chip__link:hover { border-color: var(--s-line-strong); color: var(--s-text); }

/* --- Record split: work area + rail --------------------------------------
   A long record page where a form or a table is the work and a small panel
   has to stay in reach: la nota y su control de aprobación. It was
   note-workspace-grid in the retired sheet. The rail has a floor of 280px so
   its labels do not break one character per line, and the whole thing stacks
   below 1024px, where two columns leave neither one usable. */

.s-split {
    display: grid;
    gap: var(--s-4);
    grid-template-columns: minmax(0, 1fr) clamp(280px, 24vw, 380px);
    align-items: start;
}
.s-split__main { min-width: 0; }
.s-split__rail { min-width: 0; }

@media (max-width: 1023.98px) {
    .s-split { grid-template-columns: minmax(0, 1fr); }
}

/* --- Grid of evidence cards ---------------------------------------------- */

.s-media-grid {
    display: grid;
    gap: var(--s-3);
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    grid-auto-rows: 1fr;
    align-items: stretch;
    justify-content: start;
}

@media (min-width: 1200px) {
    .s-media-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.s-media-card {
    display: grid;
    grid-template-rows: auto auto 1fr auto;
    gap: var(--s-3);
    height: 100%;
    min-height: 380px;
    padding: var(--s-3);
    background: var(--s-surface);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-md);
}

.s-media-card__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--s-2);
    padding-bottom: var(--s-2);
    border-bottom: 1px solid var(--s-line);
}

.s-media-card__thumb {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 180px;
    overflow: hidden;
    background: var(--s-sunken);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-sm);
}
.s-media-card__thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.s-media-card__link { display: block; width: 100%; }

.s-media-card__empty {
    padding: var(--s-4);
    text-align: center;
    color: var(--s-text-muted);
    font-size: var(--s-fs-sm);
}

.s-media-card__meta {
    display: grid;
    gap: var(--s-2);
    grid-template-columns: repeat(2, minmax(0, 1fr));
    padding-top: var(--s-2);
    border-top: 1px solid var(--s-line);
}
@media (max-width: 767.98px) {
    .s-media-card__meta { grid-template-columns: 1fr; }
}

.s-media-upload {
    display: grid;
    gap: var(--s-2);
    margin-top: var(--s-1);
}

/* The real <input type="file"> is replaced by a button that opens the sheet,
   so it is removed from the layout and from the accessibility tree on
   purpose: the button is what carries the label. */
.s-file-input { display: none; }

/* --- Thumbnail strip (extra evidence on a nota) --------------------------- */

.s-thumb-grid {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3);
}

.s-thumb {
    display: flex;
    flex-direction: column;
    gap: var(--s-1);
    width: 140px;
}

.s-thumb__img {
    width: 100%;
    height: 96px;
    object-fit: cover;
    background: var(--s-sunken);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-sm);
}

.s-thumb__actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
}

/* --- Picker sheet ---------------------------------------------------------
   Centred dialog on a desktop; on a phone it rises from the bottom edge,
   where the thumb already is. */

.s-sheet {
    position: fixed;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    z-index: 1050;
}
.s-sheet.is-open { display: flex; }

.s-sheet__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(19, 26, 36, 0.5);
}

.s-sheet__panel {
    position: relative;
    z-index: 1;
    width: min(360px, 92vw);
    padding: var(--s-4);
    background: var(--s-surface);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-lg);
    box-shadow: var(--s-e-3);
}

.s-sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-2);
    margin-bottom: var(--s-1);
}
.s-sheet__head h3 {
    margin: 0;
    font-size: var(--s-fs-lg);
    font-weight: 600;
}

.s-sheet__close {
    min-width: var(--s-control-h);
    min-height: var(--s-control-h);
    padding: 0;
    background: none;
    border: 0;
    color: var(--s-text-muted);
    font-size: var(--s-fs-xl);
    line-height: 1;
}
.s-sheet__close:hover { color: var(--s-text); }

.s-sheet__actions {
    display: grid;
    gap: var(--s-2);
    margin-top: var(--s-3);
}

.s-sheet__hint { margin-top: var(--s-2); }

@media (max-width: 767.98px) {
    .s-sheet { align-items: flex-end; }
    .s-sheet__panel {
        width: 100%;
        border-radius: var(--s-r-lg) var(--s-r-lg) 0 0;
    }
}

/* ==========================================================================
   22. TÁCTIL — TELÉFONO Y TABLET
   --------------------------------------------------------------------------
   Ergonomía táctil por debajo de 1024px. Este bloque contiene únicamente
   media queries max-width y vive al final de la hoja a propósito: gana el
   cascade sin tocar una sola regla base, así que el escritorio (≥1024px) es
   idéntico byte a byte. Tres capas: el teléfono corrige los controles que la
   regla móvil de 44px no alcanzaba; la tablet (768–1023.98) recibe la misma
   talla táctil sin perder el modo tabla; y las bandas de indicadores dejan de
   rematar en una celda viuda con hueco.
   ========================================================================== */

@media (max-width: 767.98px) {
    /* Los controles -sm declaran su talla DESPUÉS del bloque móvil de 44px
       (§forms), así que en el teléfono seguían en 30px: los formularios de
       abono de la nota, los selectores de exportación y los filtros de
       bitácora y tratos. Reafirmado aquí, la talla móvil gana siempre. */
    .form-control,
    .form-select,
    .form-control-sm,
    .form-select-sm {
        font-size: var(--s-fs-md);
        height: var(--s-control-h-lg);
        min-height: var(--s-control-h-lg);
    }
    textarea.form-control,
    textarea.form-control-sm { height: auto; }
    .form-control-sm { padding-inline: 0.625rem; }
    .form-select-sm { padding-left: 0.625rem; }

    /* Todo botón del contenido es un objetivo táctil: 44px el estándar y
       40px la talla chica (píldoras del índice de secciones, acciones de
       encabezado, exportadores). El alto fijo pasa a mínimo para que un
       texto a dos líneas no desborde su botón. */
    main .btn { height: auto; min-height: var(--s-control-h-lg); }
    main .btn-sm { height: auto; min-height: 40px; }
    main .btn-icon { width: var(--s-control-h-lg); min-width: var(--s-control-h-lg); }
    main .btn-icon.btn-sm { width: 40px; min-width: 40px; }

    /* En modo tarjeta el menú "⋯" quedaba como un botón de 30px huérfano en
       la esquina de la tarjeta: el idioma de escritorio "width: 1%" de la
       columna de acciones seguía ganando el cascade dentro de la tarjeta.
       La fila de acciones recupera el ancho y se estira completa; el
       envoltorio .s-actions (inline-flex a contenido) también se suelta. */
    .s-table-cards .table tbody td.s-cell-actions {
        width: 100%;
        white-space: normal;
    }
    .s-table-cards tbody td.s-cell-actions .s-actions {
        display: flex;
        flex: 1 1 100%;
        min-width: 0;
        flex-wrap: wrap;
        gap: var(--s-2);
        white-space: normal;
    }
    .s-table-cards tbody td.s-cell-actions .s-actions > * { flex: 1 1 auto; }
    .s-table-cards tbody td.s-cell-actions .s-rowmenu { flex: 1 1 auto; }
    .s-table-cards tbody td.s-cell-actions .s-rowmenu__toggle { width: 100%; }
    .s-table-cards tbody td.s-cell-actions form { display: flex; flex: 1 1 auto; }
    .s-table-cards tbody td.s-cell-actions form .btn { flex: 1 1 auto; }

    /* Una celda de tarjeta con control de captura deja el rompecabezas flex
       y apila como bloque: etiqueta arriba, control a lo ancho, texto de
       ayuda debajo. Sin esto, el input de kg del editor de nota quedaba
       aplastado a unos pixeles junto a su texto de ayuda desbordado. */
    .s-table-cards tbody td:has(.form-control, .form-select) {
        display: block;
        text-align: left;
    }
    .s-table-cards tbody td:has(.form-control, .form-select)::before {
        display: block;
        max-width: none;
        margin-bottom: 2px;
    }
    .s-table-cards tbody td .form-control,
    .s-table-cards tbody td .form-select { width: 100%; }
    .s-table-cards tbody td .form-text,
    .s-table-cards tbody td small { text-align: left; }

    /* Un enlace dentro de una tarjeta (el folio, "#9", "Ver evidencia")
       crece a objetivo táctil sin cambiar de aspecto: el margen negativo
       cancela el relleno, así que el texto no se mueve un pixel. */
    .s-table-cards tbody td a:not(.btn) {
        display: inline-flex;
        align-items: center;
        min-height: 36px;
        padding-inline: 0.375rem;
        margin-inline: -0.375rem;
    }
    .s-card-more { min-height: 40px; }

    /* Las opciones del menú "⋯" también se tocan con el pulgar. */
    .s-rowmenu__item { padding-block: 0.6875rem; }

    /* La casilla nativa de 13px es impracticable con el dedo. */
    main .form-check-input { width: 1.375rem; height: 1.375rem; }

    /* El pie de totales metía sus cuatro celdas en un renglón que desbordaba
       el teléfono: el rótulo toma su línea y las cifras fluyen debajo. */
    .s-table-cards tfoot tr { flex-wrap: wrap; row-gap: 2px; }
    .s-table-cards tfoot td { width: auto; flex: 0 1 auto; }
    .s-table-cards tfoot td:first-child { flex: 1 1 100%; font-weight: 600; }
}

/* La tablet es táctil: misma talla de control que el teléfono, sin perder el
   modo tabla ni el layout de escritorio. A partir de 1024px no cambia nada. */
@media (min-width: 768px) and (max-width: 1023.98px) {
    main .form-control,
    main .form-select,
    main .form-control-sm,
    main .form-select-sm {
        font-size: var(--s-fs-md);
        height: var(--s-control-h-lg);
        min-height: var(--s-control-h-lg);
    }
    main textarea.form-control,
    main textarea.form-control-sm { height: auto; }

    main .btn { height: auto; min-height: var(--s-control-h-lg); }
    main .btn-sm { height: auto; min-height: 40px; }
    main .btn-icon { width: var(--s-control-h-lg); min-width: var(--s-control-h-lg); }
    main .btn-icon.btn-sm { width: 40px; min-width: 40px; }

    .s-rowmenu__item { padding-block: 0.6875rem; }

    /* Un punto más de apriete en el modo compacto: con ello un libro mayor
       de 9–10 columnas cabe en una tablet vertical sin scroll lateral. */
    .s-table-compact > thead > tr > th,
    .s-table-compact > tbody > tr > td { padding-inline: 0.3125rem; }

    /* Excepción de talla: un control dentro de una fila de tabla conserva la
       letra compacta (la de 16px ensanchaba el editor de nota +400px) pero
       sube a 40px de alto para el pulgar. */
    main .table td .form-control-sm,
    main .table td .form-select-sm {
        font-size: var(--s-fs-sm);
        height: 40px;
        min-height: 40px;
    }

    /* Un enlace dentro de una celda (folio, "Nota #9", kg con desglose)
       crece a objetivo táctil sin mover el texto: margen negativo contra
       relleno equivalente. */
    main .table tbody td a:not(.btn) {
        display: inline-block;
        padding: 0.5rem 0.375rem;
        margin: -0.5rem -0.375rem;
    }

    /* La casilla nativa de 13px es impracticable con el dedo. */
    main .form-check-input { width: 1.375rem; height: 1.375rem; }
}

/* Con recuento impar colapsado a dos columnas, la última lectura de una banda
   dejaba media banda hueca; ahora cierra el renglón completo, como ya lo
   hacía el teléfono (<576px). */
@media (max-width: 991.98px) {
    .s-stats--fixed-4 > :last-child:nth-child(odd),
    .s-stats--fixed-3 > :last-child:nth-child(odd),
    .s-stats[data-cols="3"] > :last-child:nth-child(odd),
    .s-stats[data-cols="4"] > :last-child:nth-child(odd) { grid-column: span 2; }
}
