/* =====================================================================
   TRACKVY — Accendo brand layer (single source of truth)
   Loaded LAST (after style.css + style-preset.css) so it wins the cascade.
   No SCSS build exists: we remap Bootstrap (--bs-*) and Mantis (--pc-*)
   runtime variables onto the brand tokens defined below. Change a colour
   ONCE here and it propagates everywhere. Do not hardcode hexes in rules
   below — reference the tokens.
   ===================================================================== */

:root {
    /* ===== Brand tokens — the ONLY place raw hexes live ===== */
    --brand-navy: #15243C;          /* primary: buttons, active nav, headings, key figures (Accendo navy) */
    --brand-navy-600: #101C30;      /* hover  (~6% darker) */
    --brand-navy-700: #0C1525;      /* active (~12% darker) */
    --brand-ink: #161A22;           /* strongest text / headings (Accendo ink — cool) */
    --brand-blue-deep: #2C5A82;     /* interactive accent: links, focus ring, secondary (AA ~5.9:1) */
    --brand-blue-deep-600: #244A6B; /* blue-deep hover */
    --brand-blue: #498DBE;          /* logo blue — DECORATIVE only (fails AA for text) */
    --brand-blue-bright: #6EA4CB;   /* light accent on dark surfaces (Accendo hero accent) — decorative */

    /* ===== Accendo data-viz spectrum — for charts only (cool, desaturated) ===== */
    --brand-indigo: #393664;        /* deepest brand hue */
    --brand-teal: #10658C;
    --brand-teal-mid: #257396;
    --brand-sage: #85ADA3;          /* calm green-grey — tertiary chart hue */

    /* ===== Calm positive (success/in-stock/live) — replaces the vivid emerald
       everywhere EXCEPT the logo and the dashboard AI highlight. Sage/teal-leaning,
       desaturated, AA on its own tint; pairs with the desaturated danger red. ===== */
    --brand-positive: #2F7A5B;          /* calm positive solid (dots, fills, chart slice) */
    --brand-positive-600: #266449;      /* hover/active */
    --brand-positive-tint: #E7F0EB;     /* soft positive fill (rest state) */
    --brand-positive-tint-border: #CCE0D6;
    --brand-positive-text: #1E6B52;     /* AA positive text on the soft tint */
    --brand-positive-rgb: 47, 122, 91;

    --brand-green: #17CC98;         /* Trackvy vivid emerald — RESERVED: logo + dashboard AI highlight ONLY */
    --brand-navy-tint: #E7EDF3;     /* light navy fill behind navy text (replaces #e8f4ff) */
    --brand-navy-tint-border: #CBD8E5;
    /* (the old green success/hover/tint tokens were retired — success now uses the
       calm-positive family above; vivid --brand-green is logo + AI highlight only.) */
    --brand-danger-tint: #F6E4E0;   /* soft danger fill (matches danger alert) */
    --brand-danger-tint-border: #E8C9C2;

    /* Tinte de aviso (warn), calmo, no alarmante. Reemplaza hexes sueltos en .status-pill.is-warn. */
    --brand-warn-tint: #F6EEDD;
    --brand-warn-text: #8A6418;
    --brand-warn-border: #E8D9B6;

    /* Tokens de pill neutro (estado por defecto de .status-pill y de los iconos de fila en reposo). */
    --brand-pill-neutral-text: #6B6B66;
    --brand-pill-neutral-border: #E0E0DC;

    --brand-body: #4F555E;          /* body copy (cool — Accendo) */
    --brand-muted: #878C94;         /* labels, meta, captions, placeholders (cool) */
    --brand-line: #E6E8EC;          /* borders, dividers, table rules (cool) */
    --brand-line-soft: #EFF1F4;     /* lighter inner rules / zebra base (cool) */
    --brand-paper: #F7F8FB;         /* raised surface, table header fill, zebra rows (cool) */
    --brand-canvas: #F2F5F9;        /* recessed page canvas behind white cards (cool) */
    --brand-row-hover: #E7EDF3;     /* table row hover (navy tint — reads on the gray canvas) */
    --brand-th-bg: #FAFBFC;         /* very light grey table-header fill */
    --brand-white: #FFFFFF;

    /* semantic — desaturated to sit with the palette */
    --brand-success: var(--brand-positive);
    --brand-info: var(--brand-blue-deep);
    --brand-warning: #B5852A;
    --brand-danger: #A6402F;

    /* rgb helpers for rgba() alpha use */
    --brand-navy-rgb: 21, 36, 60;
    --brand-blue-deep-rgb: 44, 90, 130;

    /* categorical chart palette — an all-blue ramp (Accendo blues), ordered with
       alternating light/dark so adjacent slices stay distinguishable. Point Chart.js
       / ApexCharts at these; both cycle the ramp when there are more series than
       tones (e.g. tenant-defined extra states). */
    --chart-1: #2C5A82;   /* blue-deep — dominant slice */
    --chart-2: #7FB0D6;   /* light blue */
    --chart-3: #10658C;   /* teal-deep blue */
    --chart-4: #4E8CBE;   /* blue */
    --chart-5: #1C3E5E;   /* deep slate-blue */
    --chart-6: #A7C8E0;   /* pale blue */
    --chart-grid: var(--brand-line-soft);

    /* Rampa de OCUPACIÓN (heat) del plano de planta: secuencial cálida (gris-arena -> oro de marca) deliberadamente
       FUERA de la familia navy/azul, porque trazos y selección de zona son navy y el heat debe leerse aparte. Calma
       y desaturada (nunca alarmante): es densidad de etiquetas, no un estado de error. La usa js/location/map.js. */
    --heat-1: #EAE3D4;   /* arena pálida — ocupación mínima */
    --heat-2: #DCC79A;
    --heat-3: #CBA862;
    --heat-4: #B5852A;   /* oro de marca (--brand-warning) */
    --heat-5: #8A6315;   /* oro profundo — ocupación máxima */

    /* STATE COLOURS: the one map from a tag state to its solid colour, read by the dashboard
       donut and area (js/common/chart-theme.js) and by the detail pages (.seg-1 to .seg-6). Lent and In transit
       sit far apart in lightness, Retired is the neutral grey, never red, and Inventory keeps the calm
       positive. Los estados del tenant toman Other; Initial usa el índigo de marca, nunca un tono de --heat-*. */
    --state-inventory: var(--brand-positive);
    --state-lent: var(--chart-1);
    --state-transition: var(--chart-2);
    --state-retired: var(--brand-muted);
    --state-initial: var(--brand-indigo);   /* tono de marca, nunca el arena de --heat-* */
    --state-other: var(--chart-5);

    /* fonts */
    --brand-font-sans: "Geist", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --brand-font-mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

    /* scrollbars: one family for the sidebar, the grids and every other scroll box (see SCROLLBARS) */
    --scrollbar-size: 8px;
    --scrollbar-thumb: rgba(var(--brand-navy-rgb), 0.22);
    --scrollbar-thumb-hover: rgba(var(--brand-navy-rgb), 0.4);

    /* measures */
    --border-radius: 0.375rem;      /* 6px — crisp, not pill */
    --padding: 0.3125rem;           /* 5px */
    --pc-sidebar-width: 260px;      /* Mantis sidebar — chrome offset for header/content/footer */
    /* One page gutter: the content padding and the header padding both read it, so the hamburger and the
       page title share a left edge at every width (it drops to 15px where the sidebar becomes a drawer). */
    --page-gutter: 35px;
    --container-gutter: .75rem;     /* half of Bootstrap's 1.5rem .container-fluid gutter, where page text starts */
    --header-link-inset: 11px;      /* .pc-head-link margin (4px) + the gap that centres its 20px glyph in 34px (7px) */

    /* ===== Legacy aliases — keep old class/var names working, recoloured.
       The 56 templates using .btn-trackvy / .btn-secondary-trackvy etc.
       recolour automatically through these. ===== */
    --black-trackvy-color: var(--brand-ink);
    --blue-gray: var(--brand-body);
    --main-color: var(--brand-navy);
    --main-color-hover: var(--brand-navy-600);
    --secondary-color: var(--brand-blue-deep);
    --secondary-color-hover: var(--brand-blue-deep-600);
    --secondary-text: var(--brand-muted);
    --title-color: var(--brand-ink);
    --orange-red: #FE5621;          /* legacy orange — not part of unification */

    /* ===== Map Bootstrap global vars onto brand ===== */
    --bs-body-color: var(--brand-body);
    --bs-body-bg: var(--brand-white);
    --bs-border-color: var(--brand-line);
    --bs-font-sans-serif: var(--brand-font-sans);
    --bs-body-font-family: var(--brand-font-sans);
    --bs-font-monospace: var(--brand-font-mono);
    --bs-focus-ring-color: rgba(var(--brand-blue-deep-rgb), .35);

    /* Mantis chrome */
    --pc-heading-color: var(--brand-ink);
    --pc-sidebar-active-color: var(--brand-navy);
}

/* ===== Re-skin the active Mantis preset (body has data-pc-preset="preset-1").
   Scoped to the same selector so we match preset specificity and, loaded
   last, win. ===== */
[data-pc-preset="preset-1"] {
    --bs-primary: var(--brand-navy);
    --bs-primary-rgb: var(--brand-navy-rgb);
    --bs-primary-light: var(--brand-paper);
    --bs-blue: var(--brand-navy);

    --bs-link-color: var(--brand-blue-deep);
    --bs-link-color-rgb: var(--brand-blue-deep-rgb);
    --bs-link-hover-color: var(--brand-navy);
    --bs-link-hover-color-rgb: var(--brand-navy-rgb);

    --bs-success: var(--brand-positive);
    --bs-success-rgb: var(--brand-positive-rgb);

    --pc-sidebar-active-color: var(--brand-navy);
    --dt-row-selected: var(--brand-navy-rgb);
}

[data-pc-preset="preset-1"] .btn-primary {
    --bs-btn-bg: var(--brand-navy);
    --bs-btn-border-color: var(--brand-navy);
    --bs-btn-hover-bg: var(--brand-navy-600);
    --bs-btn-hover-border-color: var(--brand-navy-600);
    --bs-btn-active-bg: var(--brand-navy-700);
    --bs-btn-active-border-color: var(--brand-navy-700);
    --bs-btn-disabled-bg: var(--brand-navy);
    --bs-btn-disabled-border-color: var(--brand-navy);
    --bs-btn-focus-shadow-rgb: var(--brand-blue-deep-rgb);
}

/* Success button — soft calm-positive tint at rest (AA positive text), fills the
   calm positive solid on hover (white text). A matched soft->solid pair with
   .btn-danger. Vivid emerald is no longer used here (reserved for logo + AI). */
[data-pc-preset="preset-1"] .btn-success {
    --bs-btn-color: var(--brand-positive-text);
    --bs-btn-bg: var(--brand-positive-tint);
    --bs-btn-border-color: var(--brand-positive-tint-border);
    --bs-btn-hover-color: #ffffff;
    --bs-btn-hover-bg: var(--brand-positive);
    --bs-btn-hover-border-color: var(--brand-positive);
    --bs-btn-active-color: #ffffff;
    --bs-btn-active-bg: var(--brand-positive-600);
    --bs-btn-active-border-color: var(--brand-positive-600);
    --bs-btn-disabled-color: var(--brand-positive-text);
    --bs-btn-disabled-bg: var(--brand-positive-tint);
    --bs-btn-disabled-border-color: var(--brand-positive-tint-border);
}

/* Danger button — soft, alert-style tint by default; fills solid on hover.
   Calm at rest, unmistakable on intent. */
[data-pc-preset="preset-1"] .btn-danger {
    --bs-btn-color: var(--brand-danger);
    --bs-btn-bg: var(--brand-danger-tint);
    --bs-btn-border-color: var(--brand-danger-tint-border);
    --bs-btn-hover-color: #ffffff;
    --bs-btn-hover-bg: var(--brand-danger);
    --bs-btn-hover-border-color: var(--brand-danger);
    --bs-btn-active-color: #ffffff;
    --bs-btn-active-bg: var(--brand-danger);
    --bs-btn-active-border-color: var(--brand-danger);
    --bs-btn-disabled-color: var(--brand-danger);
    --bs-btn-disabled-bg: var(--brand-danger-tint);
    --bs-btn-disabled-border-color: var(--brand-danger-tint-border);
}

[data-pc-preset="preset-1"] .btn-outline-primary {
    --bs-btn-color: var(--brand-navy);
    --bs-btn-border-color: var(--brand-navy);
    --bs-btn-hover-bg: var(--brand-navy);
    --bs-btn-hover-border-color: var(--brand-navy);
    --bs-btn-active-bg: var(--brand-navy);
    --bs-btn-active-border-color: var(--brand-navy);
}

[data-pc-preset="preset-1"] .link-primary {
    color: var(--brand-blue-deep) !important;
}

/* Success / danger badges — soft tints matching the buttons & status pills,
   so the whole success/danger language is consistent (scoped to .badge only,
   so other .bg-success/.bg-danger fills are unaffected). */
[data-pc-preset="preset-1"] .badge.bg-success {
    background-color: var(--brand-positive-tint) !important;
    color: var(--brand-positive-text) !important;
}

[data-pc-preset="preset-1"] .badge.bg-danger {
    background-color: var(--brand-danger-tint) !important;
    color: var(--brand-danger) !important;
}

[data-pc-preset="preset-1"] .bg-light-primary {
    background: var(--brand-navy-tint);
    color: var(--brand-navy);
}

/* Components that hardcode the preset blue and aren't reached by --bs-primary.
   (form-check/switch, .bg-primary, success/danger badges already follow the
   --bs-* remaps above, so they're intentionally omitted.) */
[data-pc-preset="preset-1"] .progress {
    --bs-progress-bar-bg: var(--brand-navy);
}

[data-pc-preset="preset-1"] .nav-pills {
    --bs-nav-pills-link-active-bg: var(--brand-navy);
}

[data-pc-preset="preset-1"] .nav {
    --bs-nav-link-hover-color: var(--brand-navy);
}

[data-pc-preset="preset-1"] .list-group {
    --bs-list-group-active-bg: var(--brand-navy);
    --bs-list-group-active-border-color: var(--brand-navy);
}

[data-pc-preset="preset-1"] .list-group-item-primary {
    color: var(--brand-navy);
    background-color: var(--brand-navy-tint);
}

[data-pc-preset="preset-1"] .pagination {
    --bs-pagination-hover-color: var(--brand-navy);
    --bs-pagination-focus-color: var(--brand-navy);
    --bs-pagination-focus-box-shadow: 0 0 0 0.2rem rgba(var(--brand-blue-deep-rgb), 0.25);
    --bs-pagination-active-bg: var(--brand-navy);
    --bs-pagination-active-border-color: var(--brand-navy);
}

/* Align icon + label inside every button AND unify the icon/text spacing.
   inline-flex centres the Tabler glyph on the text baseline; a single `gap`
   gives identical icon-to-text spacing everywhere, and we neutralise the
   ad-hoc me-1/me-2 margins (applied inconsistently across templates) so the
   gap is the one source of truth. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
}

.btn > i,
.btn > svg {
    margin-left: 0 !important;
    margin-right: 0 !important;
}

/* don't double-space before the dropdown caret (gap already spaces it) */
.btn.dropdown-toggle::after {
    margin-left: 0;
}

/* ===== Brand button classes (used across ~56 templates) ===== */
.btn-trackvy {
    background-color: var(--brand-navy);
    border-color: var(--brand-navy);
    color: var(--brand-white);
}

.btn-trackvy:hover {
    background-color: var(--brand-navy-600);
    border-color: var(--brand-navy-600);
    color: var(--brand-white);
}

.btn-secondary-trackvy {
    background-color: var(--brand-blue-deep);
    border-color: var(--brand-blue-deep);
    color: var(--brand-white);
}

.btn-secondary-trackvy:hover {
    background-color: var(--brand-blue-deep-600);
    border-color: var(--brand-blue-deep-600);
    color: var(--brand-white);
}

/* ===== Forms ===== */
.form-control {
    color: var(--brand-body);
    font-size: 14px;
}

.form-control:focus {
    border-color: var(--brand-blue-deep);
    box-shadow: 0 0 0 .2rem rgba(var(--brand-blue-deep-rgb), .25);
}

/* The date, date-time and time pickers are drawn by the browser, which only reads these two properties.
   Without them the popup shows the operating system's blue instead of the brand navy. */
input[type="date"],
input[type="datetime-local"],
input[type="time"] {
    accent-color: var(--brand-navy);
    color-scheme: light;
}

.required_field:after {
    color: var(--bs-danger);
    content: "*";
    font-size: 1rem;
    margin-right: 0.25rem;
    padding-left: 0.25rem;
}

/* ===== Sidebar active state ===== */
.pc-sidebar .pc-navbar > .pc-item.active > .pc-link {
    color: var(--brand-navy);
}

.pc-sidebar .pc-navbar > .pc-item.active:not(.pc-hasmenu) > .pc-link::after {
    background: var(--brand-navy);
}

/* ===== Layout helpers ===== */
.container {
    padding-left: 0;
    padding-right: 0;
}

.border-radius {
    border-radius: var(--border-radius);
}

.custom-padding {
    padding: 15px 15px;
}

.card .card-header {
    border-bottom: none;
    border-radius: var(--border-radius);
}

/* Unified badge: same size/shape/weight as Bootstrap .badge and .status-pill */
.custom-badge {
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1;
    padding: 0.35em 0.65em;
    border-radius: 0.375rem;
    border: 1px solid var(--brand-navy-tint-border);
    color: var(--brand-navy);
    background: var(--brand-navy-tint);
}

a {
    color: var(--brand-ink);
}

.nowrap {
    white-space: nowrap;
}

/* ===== Typography utilities ===== */
/* Mono uppercase overline — the signature label treatment */
.eyebrow,
.overline {
    font-family: var(--brand-font-mono);
    font-size: 0.6875rem;
    font-weight: 500;
    letter-spacing: 0.18em;   /* matched to the Accendo eyebrow tracking */
    text-transform: uppercase;
    color: var(--brand-muted);
}

/* Numerals in context: money, counts, dates, metrics.
   Stays in Geist SANS (readable across many tables) but uses tabular figures
   so numeric columns still align. Use this for the bulk of numeric cells. */
.num {
    font-variant-numeric: tabular-nums;
}

/* Explicit monospace — reserve for where fixed-width genuinely helps scanning:
   EPC serials, long codes/IDs. Low volume, deliberate "engineered" signal. */
.mono {
    font-family: var(--brand-font-mono);
    font-variant-numeric: tabular-nums;
}

/* KPI stat figure — the restrained "navy total band" energy */
.kpi-figure {
    font-family: var(--brand-font-mono);
    font-variant-numeric: tabular-nums;
    font-weight: 500;
    font-size: 1.875rem;
    line-height: 1.1;
    letter-spacing: -0.01em;
    color: var(--brand-navy);
}

/* KPI card icon chip — shared primitive (dashboard KPI band + entity-detail metric banners) */
.kpi-icon {
    width: 30px;
    height: 30px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    background: var(--brand-th-bg);
    color: var(--brand-muted);
    font-size: .95rem;
}

/* =====================================================================
   Airy data table (MBB style) — generous vertical rhythm, hairline rows,
   mono-uppercase header under a strong rule, no zebra/no vertical grid,
   sticky header. Apply `.table table-airy` to dense data tables and put
   `.num` on ID / code / date / numeric cells for tabular alignment.
   ===================================================================== */
/* Separate borders: with collapsed borders the 2px rule under a sticky header stays behind when the
   header sticks (the border belongs to the table, not the cell). Cells own their borders here. */
.table-airy {
    border-collapse: separate;
    border-spacing: 0;
    --bs-table-bg: transparent;
    --bs-table-hover-bg: var(--brand-row-hover);
    margin-bottom: 0;
    color: var(--brand-body);
    border-color: var(--brand-line-soft);
}

.table-airy > thead > tr > th {
    background: var(--brand-th-bg);
    color: var(--brand-muted);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    text-align: left;
    white-space: nowrap;
    padding: 1rem 1.15rem;
    border-bottom: 2px solid var(--brand-ink);
    position: sticky;
    top: 0;
    z-index: 2;
}

.table-airy > tbody > tr > td {
    padding: 1.05rem 1.15rem;
    vertical-align: middle;
    border-top: 0;
    border-bottom: 1px solid var(--brand-line-soft);
    white-space: nowrap;
}

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

/* Primary identifier cell (serial / code) — Geist sans, emphasis via WEIGHT +
   navy colour (no mono: long hex EPCs are far more legible in sans). */
.table-airy .cell-id,
.table-airy a.cell-id {
    color: var(--brand-navy);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.01em;
    text-decoration: none;
}

.table-airy .cell-id:hover,
.table-airy a.cell-id:hover {
    color: var(--brand-blue-deep);
}

/* Secondary numerics / dates — Geist sans, tabular figures, quiet weight */
.table-airy .num {
    color: var(--brand-body);
    font-weight: 400;
}

/* Right-align numeric/money columns */
.table-airy .text-end,
.table-airy th.text-end {
    text-align: right;
}

/* Money columns: the one right-alignment rule of the family (reference cost, packing unit reference cost,
   movement cost). The header and the cells carry col-money together so a figure sits under its heading.
   Two classes in the selector beat the theme's centred table headings. */
.table th.col-money,
.table td.col-money {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Status pill — calm lifecycle states (NOT error signals).
   Same size/feel as .badge / .custom-badge. Default = neutral.
   Variants are soft tints; red is intentionally absent (reserved for
   destructive actions, never for a valid state like RETIRED). */
.status-pill {
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0.02em;
    padding: 0.35em 0.65em;
    border-radius: 0.375rem;
    /* neutral / archived (e.g. RETIRED) — gray, calm */
    background: var(--brand-line-soft);
    color: var(--brand-pill-neutral-text);
    border: 1px solid var(--brand-pill-neutral-border);
}

/* active / in-stock (e.g. INVENTORY) — calm positive (sage/teal-leaning) */
.status-pill.is-ok {
    background: var(--brand-positive-tint);
    color: var(--brand-positive-text);
    border-color: var(--brand-positive-tint-border);
}

/* in-motion / assigned (e.g. TRANSITION, LENT) — calm navy-blue */
.status-pill.is-info {
    background: var(--brand-navy-tint);
    color: var(--brand-navy);
    border-color: var(--brand-navy-tint-border);
}

/* needs attention (NOT an error) — calm amber */
.status-pill.is-warn {
    background: var(--brand-warn-tint);
    color: var(--brand-warn-text);
    border-color: var(--brand-warn-border);
}

/* estado "en curso" (in-flight): punto pulsante discreto antes del texto.
   Combinable con cualquier variante de color (is-ok/is-info/is-warn); el
   punto hereda currentColor para no atarse a un tono fijo. */
.status-pill.is-live {
    display: inline-flex;
    align-items: center;
    gap: 0.4em;
}

.status-pill.is-live::before {
    content: "";
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    animation: status-pill-pulse 1.8s infinite;
}

@keyframes status-pill-pulse {
    0% { opacity: 1; }
    50% { opacity: .3; }
    100% { opacity: 1; }
}

/* =====================================================================
   SCROLLBARS: one family for every scroll box.
   Chromium path: ::-webkit-scrollbar sets the size, the track is transparent, the thumb takes the token
   colour and the arrow buttons are hidden. It must NOT be combined with the standard scrollbar-width or
   scrollbar-color: since Chrome 121 those win and the pseudo-elements are ignored, which is how the
   native bar with arrows came back. Firefox path: the standard properties, only where the engine has no
   ::-webkit-scrollbar. A new scroll box joins the group below; it does not get a scrollbar of its own.
   ===================================================================== */
.grid-scroll,
.grid-col-menu,
.pc-sidebar .navbar-content,
.lmap-rail,
.ab-tree,
.picking-drill-rows,
.presence-page .presence-inspect-tree,
.idi-uplink-notification-tags,
.notif-panel,
.items-page .items-locations-scroll {
    --scrollbar-current-thumb: var(--scrollbar-thumb);
}

.grid-scroll::-webkit-scrollbar,
.grid-col-menu::-webkit-scrollbar,
.pc-sidebar .navbar-content::-webkit-scrollbar,
.lmap-rail::-webkit-scrollbar,
.ab-tree::-webkit-scrollbar,
.picking-drill-rows::-webkit-scrollbar,
.presence-page .presence-inspect-tree::-webkit-scrollbar,
.idi-uplink-notification-tags::-webkit-scrollbar,
.notif-panel::-webkit-scrollbar,
.items-page .items-locations-scroll::-webkit-scrollbar {
    width: var(--scrollbar-size);
    height: var(--scrollbar-size);
}

.grid-scroll::-webkit-scrollbar-track,
.grid-col-menu::-webkit-scrollbar-track,
.pc-sidebar .navbar-content::-webkit-scrollbar-track,
.lmap-rail::-webkit-scrollbar-track,
.ab-tree::-webkit-scrollbar-track,
.picking-drill-rows::-webkit-scrollbar-track,
.presence-page .presence-inspect-tree::-webkit-scrollbar-track,
.idi-uplink-notification-tags::-webkit-scrollbar-track,
.notif-panel::-webkit-scrollbar-track,
.items-page .items-locations-scroll::-webkit-scrollbar-track,
.grid-scroll::-webkit-scrollbar-corner,
.grid-col-menu::-webkit-scrollbar-corner,
.lmap-rail::-webkit-scrollbar-corner {
    background: transparent;
}

.grid-scroll::-webkit-scrollbar-button,
.grid-col-menu::-webkit-scrollbar-button,
.pc-sidebar .navbar-content::-webkit-scrollbar-button,
.lmap-rail::-webkit-scrollbar-button,
.ab-tree::-webkit-scrollbar-button,
.picking-drill-rows::-webkit-scrollbar-button,
.presence-page .presence-inspect-tree::-webkit-scrollbar-button,
.idi-uplink-notification-tags::-webkit-scrollbar-button,
.notif-panel::-webkit-scrollbar-button,
.items-page .items-locations-scroll::-webkit-scrollbar-button {
    display: none;
}

.grid-scroll::-webkit-scrollbar-thumb,
.grid-col-menu::-webkit-scrollbar-thumb,
.pc-sidebar .navbar-content::-webkit-scrollbar-thumb,
.lmap-rail::-webkit-scrollbar-thumb,
.ab-tree::-webkit-scrollbar-thumb,
.picking-drill-rows::-webkit-scrollbar-thumb,
.presence-page .presence-inspect-tree::-webkit-scrollbar-thumb,
.idi-uplink-notification-tags::-webkit-scrollbar-thumb,
.notif-panel::-webkit-scrollbar-thumb,
.items-page .items-locations-scroll::-webkit-scrollbar-thumb {
    background-color: var(--scrollbar-current-thumb);
    border-radius: var(--scrollbar-size);
    border: 2px solid transparent;   /* a slimmer thumb inside the track, no arrows */
    background-clip: content-box;
}

.grid-scroll::-webkit-scrollbar-thumb:hover,
.grid-col-menu::-webkit-scrollbar-thumb:hover,
.pc-sidebar .navbar-content::-webkit-scrollbar-thumb:hover,
.lmap-rail::-webkit-scrollbar-thumb:hover,
.ab-tree::-webkit-scrollbar-thumb:hover,
.picking-drill-rows::-webkit-scrollbar-thumb:hover,
.presence-page .presence-inspect-tree::-webkit-scrollbar-thumb:hover,
.idi-uplink-notification-tags::-webkit-scrollbar-thumb:hover,
.notif-panel::-webkit-scrollbar-thumb:hover,
.items-page .items-locations-scroll::-webkit-scrollbar-thumb:hover {
    background-color: var(--scrollbar-thumb-hover);
}

@supports not selector(::-webkit-scrollbar) {
    .grid-scroll,
    .grid-col-menu,
    .pc-sidebar .navbar-content,
    .lmap-rail,
    .ab-tree,
    .picking-drill-rows,
    .presence-page .presence-inspect-tree,
    .idi-uplink-notification-tags,
    .notif-panel,
    .items-page .items-locations-scroll {
        scrollbar-width: thin;
        scrollbar-color: var(--scrollbar-current-thumb) transparent;
    }
}

/* =====================================================================
   Airy table: static enhancements (no JS): zebra, sticky header,
   frozen first/last columns, muted empty placeholder.
   Scroll viewport enables sticky header (vertical) + frozen cols (horizontal).
   ===================================================================== */
/* Scroll region for big grids: frozen columns (overflow-x) + a sticky header
   that floats down on vertical scroll. js/common/table-grid.js measures the free height of the viewport
   and publishes it as --grid-free-height, so short tables show fully (no clipping) and only tables taller
   than the free viewport scroll INSIDE the region, which is what lets the header stick and keeps the page
   from scrolling twice. Before the script runs, or without it, the fallback is the viewport minus
   --grid-offset (the chrome above the table). --grid-min-height keeps a short window usable: below it the
   page scrolls instead of the grid collapsing. */
.grid-scroll {
    --grid-offset: 230px;
    --grid-min-height: 24rem;
    position: relative;   /* + z-index:0 => own stacking context, so the bottom */
    z-index: 0;           /* fade overlay sits above the pinned columns too */
    overflow: auto;
    max-height: max(var(--grid-min-height), var(--grid-free-height, calc(100vh - var(--grid-offset))));
    max-height: max(var(--grid-min-height), var(--grid-free-height, calc(100dvh - var(--grid-offset))));   /* dvh: iOS toolbars change vh while scrolling */
    overscroll-behavior: auto;   /* at the end of the grid the wheel continues to the page, the pointer is never trapped */
}

/* Soft zebra striping */
.table-airy.is-striped > tbody > tr:nth-child(even) > td {
    background: var(--brand-paper);
}

/* Frozen columns — opaque bg so scrolled content doesn't show through,
   plus an edge shadow so they read as pinned. */
.table-airy th.col-pin-left,
.table-airy td.col-pin-left {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--brand-white);
}

.table-airy th.col-pin-right,
.table-airy td.col-pin-right {
    position: sticky;
    right: 0;
    z-index: 2;
    background: var(--brand-white);
}

/* Edge shadows appear ONLY when there's hidden content to scroll to
   (classes toggled by js/common/table-grid.js). Left-pin shadow when scrolled
   right; right-pin shadow while content is still hidden beneath it.
   The edge is a gradient on a pseudo-element of every pinned cell, header cells included, so it is one
   continuous line from the header to the last row. A box-shadow with a negative spread stops short of
   the top and bottom of each cell and leaves a gap at every row boundary. bottom:-1px covers the row
   border. Pinned cells are position:sticky, so they are the containing block. */
.table-airy {
    --pin-shadow-width: 8px;
    --pin-shadow-color: rgba(var(--brand-navy-rgb), 0.14);
}

.table-airy th.col-pin-left::after,
.table-airy td.col-pin-left::after,
.table-airy th.col-pin-right::after,
.table-airy td.col-pin-right::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: -1px;
    width: var(--pin-shadow-width);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s;
}

.table-airy th.col-pin-left::after,
.table-airy td.col-pin-left::after {
    left: 100%;
    background: linear-gradient(to right, var(--pin-shadow-color), transparent);
}

.table-airy th.col-pin-right::after,
.table-airy td.col-pin-right::after {
    right: 100%;
    background: linear-gradient(to left, var(--pin-shadow-color), transparent);
}

.grid-scroll.is-scrolled-x .table-airy .col-pin-left::after,
.grid-scroll.has-more-right .table-airy .col-pin-right::after {
    opacity: 1;
}

/* Floating-header shadow once you've scrolled down — one fluid line across the
   whole header row, corners included (they no longer carry a side shadow). */
.grid-scroll.is-scrolled-y .table-airy thead th {
    box-shadow: 0 6px 6px -5px var(--pin-shadow-color);
}

/* "More rows below" cue — a soft bottom fade, shown only while content is hidden
   below the fold. The overlay lives on .grid-wrap (injected by table-grid.js) so
   it stays put regardless of scroll direction. */
.grid-wrap {
    position: relative;
}

.grid-fade-bottom {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 3rem;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.2s;
    /* transparent — the chevron alone signals "more below"; no white band over
       the last row */
    background: transparent;
}

/* explicit down-chevron — unmistakable "more below", even where a fade is too faint */
.grid-fade-bottom::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 9px;
    width: 10px;
    height: 10px;
    border-right: 2px solid var(--brand-body);
    border-bottom: 2px solid var(--brand-body);
    transform: translateX(-50%) rotate(45deg);
}

.grid-scroll.has-more-below + .grid-fade-bottom {
    opacity: 1;
}

/* gentle downward bob while there's more below — a live "scroll down" cue */
.grid-scroll.has-more-below + .grid-fade-bottom::after {
    animation: grid-chevron-bob 1.5s ease-in-out infinite;
}

@keyframes grid-chevron-bob {
    0%, 100% { transform: translateX(-50%) translateY(0) rotate(45deg); }
    50%      { transform: translateX(-50%) translateY(4px) rotate(45deg); }
}

@media (prefers-reduced-motion: reduce) {
    .grid-scroll.has-more-below + .grid-fade-bottom::after {
        animation: none;
    }
}

/* When the grid has a horizontal scrollbar, lift the fade + chevron above it
   (toggled by table-grid.js) so they don't sit on the scrollbar track. */
.grid-scroll.has-x-overflow + .grid-fade-bottom {
    bottom: 0.9rem;
}

/* ===== Responsive: on phones, DON'T freeze columns — the pinned column eats
   the small viewport. Let the grid scroll normally; keep the floating header. */
@media (max-width: 767.98px) {
    /* body cells: fully unpin */
    .table-airy td.col-pin-left,
    .table-airy td.col-pin-right {
        position: static;
    }
    /* header cells: KEEP sticky-top (so Serial/Acciones don't vanish on vertical
       scroll) — only drop the horizontal pin offsets */
    .table-airy th.col-pin-left,
    .table-airy th.col-pin-right {
        left: auto;
        right: auto;
    }
    /* nothing is pinned, so there is no edge to shade */
    .table-airy .col-pin-left::after,
    .table-airy .col-pin-right::after {
        display: none;
    }
}

/* Narrow tables do not freeze columns either: eight columns or fewer fit a desktop screen, and a frozen
   edge only costs width there (CLAUDE.md, data tables). The pin classes stay in the markup, because they
   also mark the identity and action columns the column menu never hides; this rule only switches the
   freeze off. A table that is not narrow keeps its pins. */
@media (min-width: 768px) {
    .table-airy:not(:has(> thead > tr > th:nth-child(9))) td.col-pin-left,
    .table-airy:not(:has(> thead > tr > th:nth-child(9))) td.col-pin-right {
        position: static;
    }
    .table-airy:not(:has(> thead > tr > th:nth-child(9))) th.col-pin-left,
    .table-airy:not(:has(> thead > tr > th:nth-child(9))) th.col-pin-right {
        left: auto;
        right: auto;
    }
    .table-airy:not(:has(> thead > tr > th:nth-child(9))) .col-pin-left::after,
    .table-airy:not(:has(> thead > tr > th:nth-child(9))) .col-pin-right::after {
        display: none;
    }
}

/* Opt-in mobile pin (data-pin-mobile="left" on the table): the identity column stays put while the
   rest scrolls sideways, capped so it cannot eat the screen. Without the attribute a phone unpins.
   The cell clips its overflow, which would cut an edge drawn outside it, so the edge sits inside. */
@media (max-width: 767.98px) {
    .table-airy[data-pin-mobile="left"] td.col-pin-left,
    .table-airy[data-pin-mobile="left"] th.col-pin-left {
        position: sticky;
        left: 0;
        max-width: 40vw;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    .table-airy[data-pin-mobile="left"] td.col-pin-left::after,
    .table-airy[data-pin-mobile="left"] th.col-pin-left::after {
        display: block;
        left: auto;
        right: 0;
        background: linear-gradient(to left, var(--pin-shadow-color), transparent);
    }
}
/* The bobbing "more below" chevron floats over a row in a short scroll box (role matrix); on a phone
   the swipe itself is the cue. */
@media (max-width: 767.98px) {
    .grid-fade-bottom { display: none; }
}

/* header corners above both the header row and the pinned body cells, and
   tinted to match the grey header (overrides the white pin background) */
.table-airy thead th.col-pin-left,
.table-airy thead th.col-pin-right {
    z-index: 4;
    background: var(--brand-th-bg);
}

/* pinned cells follow the stripe + hover so the freeze looks continuous */
.table-airy.is-striped > tbody > tr:nth-child(even) > td.col-pin-left,
.table-airy.is-striped > tbody > tr:nth-child(even) > td.col-pin-right {
    background: var(--brand-paper);
}

/* Non-pinned cells get hover from Bootstrap's inset box-shadow (--bs-table-hover-bg).
   Pinned cells paint an opaque background of their own, so name the hover colour on it too. */
.table-airy.table-hover > tbody > tr:hover > td.col-pin-left,
.table-airy.table-hover > tbody > tr:hover > td.col-pin-right {
    background: var(--brand-row-hover);
}

/* muted placeholder for empty cells (reads better than a blank gap) */
.cell-empty {
    color: var(--brand-muted);
}

/* The one empty-value placeholder: an empty .cell-empty (and an empty builder value) draws a muted plain hyphen
   (a middle dot reads as a speck; the hyphen is not an em or en dash), so no
   template or script prints a glyph of its own and no em or en dash reaches the page. */
.cell-empty:empty::before,
.ab-rail-val:empty::before,
.ab-rail-kpi-figure:empty::before,
.ab-review dd:empty::before {
    content: "-";
    color: var(--brand-muted);
}

/* wrapping description cell (overrides table-airy nowrap) — e.g. report catalog */
.table-airy td.report-desc {
    white-space: normal;
    min-width: 240px;
    max-width: 460px;
    color: var(--brand-muted);
}

/* celda con truncado multilinea (clamp a 2 lineas), reemplaza el patron de
   max-width + ellipsis en linea repetido en varias vistas. --cell-clip-width
   es el ancho por defecto, redefinible por celda si hace falta. */
.cell-clip {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    max-width: var(--cell-clip-width, 280px);
    white-space: normal;
}

/* Densidad compacta: fila más baja y letra un poco menor. La llevan dos clases de tabla:
   - la tabla con .is-compact fija en la plantilla (las tablas embebidas en las pestañas de detalle);
   - la tabla de una barra de grilla (la tarjeta que sigue a .grid-toolbar), compacta desde la primera pintura,
     salvo que el usuario haya guardado la densidad cómoda: table-controls.js le pone entonces .is-comfortable.
     Antes la compacta llegaba por script después de cargar y la tabla se veía amplia un instante. */
.table-airy.is-compact > thead > tr > th,
.table-airy.is-compact > tbody > tr > td,
.grid-toolbar + .card .table-airy:not(.is-comfortable) > thead > tr > th,
.grid-toolbar + .card .table-airy:not(.is-comfortable) > tbody > tr > td {
    padding: 0.5rem 0.6rem;
    font-size: 0.8125rem;
}

/* Las filas compactas ocupan una sola línea: las etiquetas no saltan de línea (la utilidad .flex-wrap es
   !important, así que esta regla también) y la miniatura de la referencia sigue la altura de la fila. */
.table-airy.is-compact > tbody > tr > td > .d-flex.flex-wrap,
.grid-toolbar + .card .table-airy:not(.is-comfortable) > tbody > tr > td > .d-flex.flex-wrap { flex-wrap: nowrap !important; }
.table-airy.is-compact .reference-thumb-sm,
.grid-toolbar + .card .table-airy:not(.is-comfortable) .reference-thumb-sm { width: 1.5rem; height: 1.5rem; }

/* Grid toolbar (density, columns, rows per page): the header strip of the table card. It has the card's
   surface, border, radius and shadow, and the card that follows it joins it with no gap and no second
   border, so toolbar and table read as one block on every list page. Controls sit on the right; the left
   side stays free for a result summary or filter chips. Inside a card (a toolbar placed in a card body)
   it is a plain strip with a hairline under it. */
.grid-toolbar {
    /* La altura de un control -sm de Bootstrap (letra pequeña por interlineado 1.5, más relleno y borde), la que
       ya tienen el selector de filas y el buscador: todos los controles de la barra la toman. */
    --grid-control-height: calc(0.765625rem * 1.5 + 0.75rem + 2px);
    padding: 0.5rem 0.75rem;
    background: var(--brand-white);
    border: 1px solid var(--brand-line);
    border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
    box-shadow: var(--bs-box-shadow-sm);
}

.grid-toolbar + .card {
    margin-top: 0;
    border: 1px solid var(--brand-line);
    border-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

.card .grid-toolbar {
    border: 0;
    border-bottom: 1px solid var(--brand-line);
    border-radius: 0;
    box-shadow: none;
}

/* Buscador de una lista (fragments/util/search.html): una fila delgada. El campo toma el ancho libre y el botón
   Buscar queda en su extremo derecho; el panel de filtros avanzados se abre debajo, en la misma tarjeta. */
.list-search { margin-bottom: 0.75rem; }
.list-search > .card-body { padding: 0.5rem 0.75rem; }

/* El buscador en la línea de la barra de la grilla (fragments/grid/toolbar :: searchToolbar): después de los
   controles, toma el resto de la línea; en pantalla angosta baja a una línea propia. */
.grid-toolbar .grid-search { flex: 1 1 16rem; min-width: 12rem; }

/* Una sola altura en la línea de la barra: los botones de íconos (densidad, columnas) quedaban más bajos que el
   selector de filas y el buscador. Los controles del panel de filtros avanzados no entran: no son hijos directos. */
.grid-toolbar > .btn-group > .btn,
.grid-toolbar > .dropdown > .btn,
.grid-toolbar > .grid-page-size,
.grid-toolbar > .grid-search > .input-group > .form-control,
.grid-toolbar > .grid-search > .input-group > .btn {
    height: var(--grid-control-height);
}

/* Column show/hide menu */
.grid-col-menu {
    max-height: 320px;
    min-width: 12rem;
    overflow: auto;
}

/* row action icons — calm (NOT disabled) at rest, full-strength on row hover,
   circular paper hit-target on icon hover. Works for both <a> and <button>. */
.table-airy .row-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    border: 0;                       /* kills default <button> box */
    background: transparent;
    border-radius: 50%;
    color: var(--brand-pill-neutral-text);   /* clearly an icon at rest */
    font-size: 1rem;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

/* only the hovered icon lights up (not its siblings); the row background
   already marks the active row, so no row-level wake is needed */
/* the icon itself hovered → paper circle so the hit target is obvious */
.table-airy .row-action:hover {
    background: var(--brand-paper);
    color: var(--brand-navy);
}

.table-airy .row-action.is-danger:hover {
    background: var(--brand-paper);
    color: var(--brand-danger);
}

/* ===== Tables ===== */
.table thead th {
    text-align: center;
    white-space: nowrap;
}

.table thead tr th {
    color: var(--brand-muted);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 1px;
    text-transform: uppercase;
}

.table-condensed > tbody > tr > td,
.table-condensed > tbody > tr > th,
.table-condensed > tfoot > tr > td,
.table-condensed > tfoot > tr > th,
.table-condensed > thead > tr > td,
.table-condensed > thead > tr > th {
    padding: 5px;
}

/* ===== Pagination ===== */
.page-link {
    color: var(--brand-blue-deep);
    background-color: var(--brand-white);
    border-color: var(--brand-line);
}

.page-link:hover {
    color: var(--brand-navy);
    background-color: var(--brand-paper);
    border-color: var(--brand-line);
}

.page-item.active .page-link {
    color: var(--brand-white);
    background-color: var(--brand-navy);
    border-color: var(--brand-navy);
}

.page-item.disabled .page-link {
    color: var(--brand-muted);
    background-color: var(--brand-white);
    border-color: var(--brand-line);
}

/* ===== Cards ===== */
.card-hover-fx {
    transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}

.card-hover-fx:hover {
    transform: translateY(-3px);
    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05) !important;
}

/* ===== Tabs ===== */
.mantis-line-tabs {
    border-bottom: 1px solid var(--brand-line);
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
}

.mantis-line-tabs::-webkit-scrollbar {
    display: none;
}

.mantis-line-tabs .nav-link {
    color: var(--brand-body);
    border: none;
    border-bottom: 2px solid transparent;
    padding: 12px 20px;
    font-weight: 500;
    background: transparent;
    transition: all 0.3s ease;
}

.mantis-line-tabs .nav-link.active {
    color: var(--brand-navy) !important;
    border-bottom: 2px solid var(--brand-navy) !important;
}

/* ===== Detail sidebar items ===== */
.sidebar-item {
    display: flex;
    align-items: flex-start;
    margin-bottom: 1.25rem;
}

.sidebar-item i {
    font-size: 1.25rem;
    color: var(--brand-navy);
    margin-right: 1rem;
    margin-top: 0.2rem;
}

.sidebar-item-content .title {
    font-size: 0.75rem;
    text-transform: uppercase;
    color: var(--brand-muted);
    font-weight: 600;
    margin-bottom: 0.1rem;
}

.sidebar-item-content .value {
    font-size: 0.95rem;
    color: var(--brand-ink);
    font-weight: 500;
}

/* ===== Sidebar scrollable ===== */
/* The theme sizes the scroll area as 100vh - 60px, but our logo block is 125px tall, so the last
   65px sat below the screen and the end of a long submenu (Configuración) could not be reached.
   The wrapper is a column: the scroll area takes whatever the logo leaves. */
.pc-sidebar .navbar-wrapper { display: flex; flex-direction: column; height: 100%; }
.pc-sidebar .navbar-content {
    flex: 1 1 auto;
    min-height: 0;
    height: auto;
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-gutter: stable;   /* the menu does not shift sideways when a submenu opens and the bar appears */
    --scrollbar-current-thumb: transparent;   /* the menu reads clean at rest ... */
}

/* ... and the thumb shows while the pointer is over the sidebar (family in SCROLLBARS) */
.pc-sidebar:hover .navbar-content {
    --scrollbar-current-thumb: var(--scrollbar-thumb);
}

/* ===== Pie del menú: crédito a Accendo, términos y versión =====
   El pie de página ya no existe: el crédito con el logo de Accendo, los términos y la versión
   quedan en pila, fijos bajo el menú, que se desplaza por encima. Se ven también en el cajón móvil y se ocultan
   con el menú plegado. */
.pc-sidebar .sidebar-footer {
    flex: none;
    padding: .5rem 1.5rem;
    border-top: 1px solid var(--brand-line);
    font-size: .6875rem;
    line-height: 1.5;
    color: var(--brand-muted);
}
.pc-sidebar .sidebar-footer a {
    color: var(--brand-muted);
    text-decoration: none;
}
.pc-sidebar .sidebar-footer a:hover {
    color: var(--brand-navy);
}
/* El logo de Accendo lleva el nombre y mide lo que la línea de texto. */
.pc-sidebar .sidebar-credit { display: flex; align-items: center; gap: 0.35rem; }
.pc-sidebar .sidebar-credit-logo { display: block; height: 1.5em; width: auto; }

/* ===========================
   Mantis Sidebar Arrow Fix
   =========================== */
.pc-sidebar .pc-arrow > i {
    font-size: 14px;
    line-height: 1;
    display: inline-block;
    vertical-align: middle;
    transition: transform 0.2s ease-in-out;
}

/* El giro al abrir lo hace YA el tema sobre el <span> contenedor (style.css: .pc-arrow con
   rotate(90deg)), y el icono de dentro gira con el. Aqui habia un segundo giro de 90 grados sobre
   la <i>, asi que sumaban 180: el chevron que apunta a la derecha acababa apuntando a la IZQUIERDA
   al abrir el submenu, cuando lo natural es que apunte hacia ABAJO, hacia lo que acaba de
   desplegarse. Se quita el giro sobrante; el del tema basta. */

/* ===========================================================================
   AI INSIGHTS — dashboard panel + /analytics/ai report
   All colors reference brand tokens. The vivid emerald is reserved for the
   single largest (positive) state slice; everything else is calm navy/blue.
   =========================================================================== */
.ai-spark { color: var(--brand-blue-deep); }

/* brief hero — navy accent rail + soft wash so the summary reads as the centerpiece */
.ai-hero {
    position: relative;
    border: 0 !important;
    border-left: 3px solid var(--brand-blue-deep) !important;
    background: linear-gradient(115deg, var(--brand-navy-tint) 0%, var(--brand-white) 60%);
    overflow: hidden;
}
.ai-hero::after {
    content: "\f6d7"; /* ti ti-sparkles */
    font-family: "tabler-icons"; position: absolute; top: -18px; right: -10px;
    font-size: 130px; line-height: 1; color: var(--brand-blue-deep); opacity: .05; pointer-events: none;
}
.ai-hero .card-body { position: relative; z-index: 1; }

.insight-brief { font-size: .95rem; line-height: 1.5; color: var(--brand-ink); }
.insight-brief--lg { font-size: 1.15rem; line-height: 1.55; font-weight: 500; color: var(--brand-navy); }

/* dashboard-panel highlight rows */
.hl { display: flex; gap: .55rem; align-items: flex-start; padding: .45rem 0; font-size: .85rem; color: var(--brand-body); }
.hl .dot { width: 8px; height: 8px; border-radius: 50%; margin-top: .4rem; flex: 0 0 auto; background: var(--brand-blue-deep); }
.hl.ok .dot { background: var(--brand-positive); }
.hl.warn .dot { background: var(--brand-warning, #B5852A); }
.hl.info .dot { background: var(--brand-blue-deep); }

.ai-meta { font-size: .7rem; color: var(--brand-muted); }
.ai-foot a { color: var(--brand-blue-deep); text-decoration: none; font-weight: 600; font-size: .8rem; }
.ai-foot a:hover { color: var(--brand-navy); }

/* Accendo-style sliding-arrow micro-gesture — text links only (buttons stay still
   per the design rule). The trailing arrow nudges right on hover. */
.ai-foot a i, .link-nudge i { transition: transform .25s ease; }
.ai-foot a:hover i, .link-nudge:hover i { transform: translateX(4px); }

/* dashboard AI panel — thin top accent (fixed, no hover lift — matches the other cards) */
.ai-panel { position: relative; overflow: hidden; }
.ai-panel::before {
    content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
    background: linear-gradient(90deg, var(--brand-green), var(--brand-blue-deep));
}

/* "Basado en" figures */
.snap .v { font-size: 1.25rem; font-weight: 600; color: var(--brand-navy); font-family: var(--brand-font-mono); }
.snap .l { font-size: .625rem; text-transform: uppercase; letter-spacing: .08em; color: var(--brand-muted); }

/* =====================================================================
   Editorial report narrative (markdown -> HTML via commonmark).
   Styles whatever structure the model emits: sections, lead, lists,
   ranked priorities (<ol>), key-takeaway callout (<blockquote>).
   ===================================================================== */
.report-body { font-family: var(--brand-font-sans); }
.report-body > :first-child { margin-top: 0; }

/* lead paragraph — the first paragraph reads larger, in navy ink */
.report-body > p:first-of-type { font-size: 1.02rem; line-height: 1.7; color: var(--brand-ink); }

/* section headers — eyebrow style over a hairline rule, with an accent tick */
.report-body h2, .report-body h3 {
    display: flex; align-items: center; gap: .5rem;
    font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .1em;
    color: var(--brand-blue-deep);
    margin: 1.7rem 0 .7rem; padding-top: 1rem;
    border-top: 1px solid var(--brand-line-soft);
}
.report-body h2::before, .report-body h3::before {
    content: ''; flex: 0 0 auto; width: 6px; height: 6px; border-radius: 2px;
    background: var(--brand-blue-deep);
}
.report-body h4, .report-body h5, .report-body h6 {
    font-size: .95rem; font-weight: 700; color: var(--brand-ink); margin: 1.1rem 0 .35rem;
}
.report-body p { font-size: .9rem; line-height: 1.7; color: var(--brand-body); margin-bottom: .7rem; }
.report-body strong { color: var(--brand-navy); font-weight: 600; }

/* unordered list — quiet accent dot markers */
.report-body ul { list-style: none; padding-left: 0; margin-bottom: .8rem; }
.report-body ul li {
    position: relative; padding-left: 1.1rem; margin-bottom: .4rem;
    font-size: .9rem; line-height: 1.55; color: var(--brand-body);
}
.report-body ul li::before {
    content: ''; position: absolute; left: 0; top: .55em;
    width: 5px; height: 5px; border-radius: 50%; background: var(--brand-blue);
}

/* ordered list — numbered priority chips */
.report-body ol { list-style: none; counter-reset: pri; padding-left: 0; margin-bottom: .8rem; }
.report-body ol li {
    counter-increment: pri; position: relative; padding-left: 2.1rem;
    margin-bottom: .55rem; min-height: 1.5rem;
    font-size: .9rem; line-height: 1.5; color: var(--brand-body);
}
.report-body ol li::before {
    content: counter(pri); position: absolute; left: 0; top: -.05em;
    width: 1.45rem; height: 1.45rem; border-radius: 50%;
    background: var(--brand-navy-tint); color: var(--brand-navy);
    font-family: var(--brand-font-mono); font-size: .72rem; font-weight: 600;
    display: flex; align-items: center; justify-content: center;
}

/* blockquote — key-takeaway callout */
.report-body blockquote {
    margin: 1.1rem 0; padding: .8rem 1rem;
    border-left: 3px solid var(--brand-blue-deep);
    background: var(--brand-paper); border-radius: 0 8px 8px 0;
}
.report-body blockquote p { margin: 0; font-size: .92rem; line-height: 1.6; color: var(--brand-ink); }
.report-body blockquote > :last-child { margin-bottom: 0; }

/* "Instantánea" mini-viz (renders the snapshot, not the dashboard charts) */
.viz-label { font-size: .625rem; text-transform: uppercase; letter-spacing: .08em; color: var(--brand-muted); margin-bottom: .4rem; }
.ai-stack { display: flex; height: 14px; border-radius: 7px; overflow: hidden; background: var(--brand-line-soft); }
.ai-stack span { display: block; height: 100%; }

/* state-distribution donut (CSS conic-gradient — no chart lib on this page) */
.ai-donut {
    width: 168px; height: 168px; border-radius: 50%;
    margin: .25rem auto .25rem; position: relative;
}
.ai-donut__hole {
    position: absolute; inset: 32px; border-radius: 50%; background: var(--brand-white);
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    box-shadow: inset 0 0 0 1px var(--brand-line-soft);
}
.ai-donut__num { font-family: var(--brand-font-mono); font-size: 1.5rem; font-weight: 600; color: var(--brand-navy); line-height: 1; }
.ai-donut__lbl { font-size: .6rem; text-transform: uppercase; letter-spacing: .08em; color: var(--brand-muted); margin-top: .2rem; }
.viz-legend { display: flex; flex-wrap: wrap; gap: .35rem .9rem; margin-top: .5rem; font-size: .7rem; color: var(--brand-body); }
.viz-legend i { width: 8px; height: 8px; border-radius: 2px; display: inline-block; margin-right: 4px; vertical-align: middle; }
.mini .r { display: flex; align-items: center; gap: .5rem; margin: .35rem 0; font-size: .75rem; color: var(--brand-body); }
.mini .r .lbl { width: 72px; color: var(--brand-muted); }
.mini .track { flex: 1; height: 10px; background: var(--brand-line-soft); border-radius: 5px; overflow: hidden; }
.mini .fill { height: 100%; border-radius: 5px; }
.mini .val { font-family: var(--brand-font-mono); width: 40px; text-align: right; }

/* state-slice palette: calm positive = largest/positive, rest calm blues/neutral */
.ai-seg--pos { background: var(--brand-positive); }
.ai-seg--1 { background: var(--brand-blue-deep); }
.ai-seg--2 { background: var(--brand-blue); }
.ai-seg--3 { background: var(--brand-navy-tint-border); }
.ai-seg--4 { background: var(--brand-muted); }

@media print {
    .pc-sidebar, .pc-header, .btn, nav.breadcrumb, .breadcrumb { display: none !important; }
    .pc-container, .pcoded-content { margin: 0 !important; }

    /* ===== Vista impresa de pagina: boton "Imprimir" en analytics/snapshots/{date},
       analytics/overview y analytics/ai: un window.print() general.
       .no-print cubre cualquier control que no aporte nada en papel (el selector de rango, el propio
       boton "Exportar"); cada tarjeta de datos es su propia unidad de salto de pagina, para que una
       tarjeta larga nunca se corte a la mitad entre dos hojas. A4 y Carta miden distinto de ancho, asi
       que el margen se deja al navegador (@page sin tamaño fijo) y el contenido usa el 100% del ancho
       disponible. */
    .no-print, .dropdown-menu { display: none !important; }
    .card { break-inside: avoid; page-break-inside: avoid; box-shadow: none !important; border: 1px solid #ccc !important; }
    .row.g-3 > * { break-inside: avoid; page-break-inside: avoid; }
    @page { margin: 1.2cm; }
}

/* ===========================================================================
   APP CHROME — "canvas ladder": a recessed gray page behind white cards, with
   the sidebar as the single raised white surface. header and content are
   the gray base (--brand-canvas); cards keep their soft shadow and float on it.
   The header is ABSOLUTE (not fixed) so it scrolls away with the content instead
   of pinning over it — only the sidebar stays fixed.
   =========================================================================== */
body { background: var(--brand-canvas); }

.pc-container,
.pc-header { background: var(--brand-canvas); }

.pc-header {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    box-shadow: none;
    border-bottom: 1px solid var(--brand-line);
}

/* The header sits beside the sidebar only where the sidebar is docked. At 1024px and below the sidebar is an
   off-canvas drawer, so the header starts at the left edge and the hamburger sits where the drawer opens. */
@media (min-width: 1025px) {
    .pc-header { left: var(--pc-sidebar-width); }
}

/* Page gutter. The content starts at the gutter plus the container padding; the header pads its first and
   last icon by the same amount minus the inset of the glyph inside its link, so the hamburger lines up with
   the page title and the last header icon with the right edge of the cards. Below the drawer breakpoint the
   gutter token shrinks and both follow. */
@media (max-width: 1024px) {
    :root { --page-gutter: 15px; }
}
/* Phone width: the theme gives the avatar-only profile link 40px, which leaves 26px of content box for a 24px
   avatar and puts its right edge 2px inside the page gutter. The link is the avatar plus its own 7px padding. */
@media (max-width: 575.98px) {
    .pc-header .header-user-profile .pc-head-link { width: calc(24px + 2 * 7px); }
}
.pc-container .pc-content {
    padding-left: var(--page-gutter);
    padding-right: var(--page-gutter);
    padding-bottom: var(--container-gutter);   /* aire bajo lo último de la página, ahora que no la sigue un pie */
}
.pc-header .header-wrapper {
    padding-left: calc(var(--page-gutter) + var(--container-gutter) - var(--header-link-inset));
    padding-right: calc(var(--page-gutter) + var(--container-gutter) - var(--header-link-inset));
}

/* Sidebar brand: 20px of air above and below the logo, as padding inside the white panel. It used to be an inline
   margin, which collapsed through .navbar-wrapper and let the grey page show as a band above the logo. */
.pc-sidebar .m-header {
    height: auto;
    padding-top: 36px;
    padding-bottom: 36px;
}

/* Sidebar links as a row: icon, label, arrow. A label that wraps ("Cargue de Inventario T.") stays in its own
   column instead of running back under the icon. */
.pc-sidebar .pc-link {
    display: flex;
    align-items: flex-start;
}
.pc-sidebar .pc-link .pc-micon,
.pc-sidebar .pc-link .pc-arrow { flex: none; }
.pc-sidebar .pc-link .pc-mtext { flex: 1 1 auto; min-width: 0; }

/* Page header (title plus action buttons, on 159 pages): on a phone the buttons wrap under the title instead of
   widening the page past the screen. Pagination wraps too. */
@media (max-width: 575.98px) {
    .container-fluid > .d-flex.justify-content-between.align-items-center.mb-4 {
        flex-wrap: wrap;
        gap: 0.75rem;
    }
    .container-fluid > .d-flex.justify-content-between.align-items-center.mb-4 > .d-flex { flex-wrap: wrap; }
}
/* Named page header (analytics pages): same behavior as the exact-class rule above, but by name, so
   a header with a range switch, a date jump or an export menu can wrap without copying that class
   string. Below 576px the actions take the full row and no control keeps a min-width that pushes
   the page wider than the screen. */
.page-head { flex-wrap: wrap; }
@media (max-width: 575.98px) {
    .page-head { gap: 0.75rem; }
    .page-head-actions { width: 100%; flex-wrap: wrap; }
    .page-head-actions > * { min-width: 0; }
    .page-head-range { flex: 1 1 100%; }
    .page-head-range .range-switch { display: flex; width: 100%; }
    .page-head-range .range-switch a { flex: 1 1 0; text-align: center; }
    .page-head-actions .ai-datejump { flex: 1 1 0; max-width: none; }
}
.pagination {
    flex-wrap: wrap;
    row-gap: 0.25rem;
}

/* Pie de una lista acotada (fragments/grid/bounded.html): una fila delgada, el rango de filas a la izquierda y un
   paginador compacto a la derecha, para que la tabla conserve la altura. En pantalla angosta salta de línea. */
.bounded-footer [data-bounded-body] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 1rem;
}
.bounded-footer [data-bounded-body] > [data-bounded-pager] { margin-left: auto; }
/* Dentro de una tarjeta (formularios de mover y prestar, detalle de un registro) el pie es la franja inferior de la
   tarjeta, con su propio aire y una línea encima. */
.card > .bounded-footer { padding: 0.5rem 0.75rem; border-top: 1px solid var(--brand-line); }
.bounded-footer .pagination { margin-bottom: 0; }
.bounded-footer .page-link { padding: 0.25rem 0.625rem; font-size: 0.8125rem; }

/* The off-canvas drawer dims the page; no blur, so the page behind stays legible and calm. */
.pc-menu-overlay {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    height: 100vh;
    height: 100dvh;              /* iOS: 100vh includes the toolbar area and left the overlay short */
    touch-action: none;          /* a drag on the dimmed area never scrolls the page behind */
    overscroll-behavior: contain;
}

/* iOS drawer: dvh so the menu ends above the toolbar, safe-area padding for the notch and the home
   bar, and no scroll chaining from the menu into the page (js/common/drawer-lock.js also freezes
   the body while the drawer is open). */
.pc-sidebar {
    height: 100vh;
    height: 100dvh;
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
}
.pc-sidebar .navbar-content {
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}
body.is-drawer-open { overflow: hidden; }

/* elegant lift: a crisp hairline + soft shadow separates the white menu from the beige canvas */
.pc-sidebar {
    box-shadow: 1px 0 0 var(--brand-line), 7px 0 22px -10px rgba(14, 42, 69, 0.16);
    z-index: 1029;
}

/* Desktop sidebar collapse — content/header must shed the sidebar offset */
@media (min-width: 1025px) {
    .pc-sidebar.pc-sidebar-hide {
        width: 0;
        box-shadow: none;
    }

    .pc-sidebar.pc-sidebar-hide ~ .pc-header { left: 0; }

    .pc-sidebar.pc-sidebar-hide ~ .pc-container { margin-left: 0; }

    /* body class toggled by pcoded.js — backup when sibling selectors miss */
    body.is-sidebar-hidden .pc-header { left: 0; }

    body.is-sidebar-hidden .pc-container { margin-left: 0; }
}

/* sidebar menu hover — navy tint so it reads against the gray (was near-invisible grey) */
.pc-sidebar .pc-navbar .pc-item .pc-link:hover {
    background: var(--brand-navy-tint);
    color: var(--brand-navy);
}

/* ===========================================================================
   SHARED CHROME & COMPONENTS — moved here from inline styles in shared
   partials/fragments (layout, progress modal, grid toolbar, search). DRY.
   =========================================================================== */
/* sidebar icons/arrows are decorative — clicks fall through to the link */
.pc-micon i, .pc-micon svg, .pc-arrow i, .pc-arrow svg { pointer-events: none; }
/* grid toolbar page-size select shouldn't stretch to fill the bar */
.grid-page-size { width: auto; }
/* search fragment row-count select */
.limit-select { max-width: 90px; }

/* ===========================================================================
   DASHBOARD: / (exec overview). Scoped under .dashboard-page so generic
   selectors (.card-header, .state-container, .filter-group) don't leak app-wide.
   Moved here from the page's inline <style> (DRY: styles live in CSS).
   =========================================================================== */
.dashboard-page .kpi-card { transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease; }
.dashboard-page .kpi-card:hover { transform: translateY(-2px); box-shadow: 0 10px 24px rgba(14, 42, 69, .06) !important; }

/* Affordance generica para tarjetas KPI clicables (detalle de auditoria, etc.): el hover comunica que
   la tarjeta navega. Solo aplica cuando la tarjeta es o esta dentro de un enlace, para no sugerir
   clic en tarjetas KPI puramente informativas (p. ej. metricas de tag/detail). */
a.kpi-card, a .kpi-card { transition: transform .2s ease, box-shadow .2s ease; }
a.kpi-card:hover, a .kpi-card:hover { transform: translateY(-2px); box-shadow: 0 10px 24px rgba(14, 42, 69, .06); }

/* Popups del mapa del dashboard y del globo de ubicaciones (dashboardmap.js, globe.js, map-kit.js
   sitePopupHtml): globales a proposito, mapbox monta los popups fuera del alcance de .dashboard-page.
   Contrato de clases con esos tres archivos. Un popup de sedes lista una o varias raices que comparten
   el punto; cada .dash-map-popup-site es una, separadas por una linea. */
.dash-map-popup { font-family: var(--brand-font-sans); min-width: 190px; color: var(--brand-ink); }
.dash-map-popup-wide { min-width: 210px; }
.dash-map-popup-sites { max-height: 320px; overflow-y: auto; }
.dash-map-popup-site + .dash-map-popup-site { border-top: 1px solid var(--brand-line); margin-top: 8px; padding-top: 8px; }
.dash-map-popup-title { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; text-decoration: none; color: var(--brand-navy); font-weight: 700; }
a.dash-map-popup-title:hover { color: var(--brand-blue-deep); }
.dash-map-popup-meta { font-size: .78rem; color: var(--brand-body); margin-bottom: 6px; }
.dash-map-popup-meta-bordered { border-top: 1px solid var(--brand-line-soft); padding-top: 6px; margin-bottom: 0; }
.dash-map-popup-link { display: flex; align-items: center; gap: 8px; text-decoration: none; color: var(--brand-navy); font-weight: 600; }
.dash-map-popup-link-split { justify-content: space-between; padding: 6px 0; border-top: 1px solid var(--brand-line-soft); gap: 0; }
.dash-map-popup-inline-link { color: var(--brand-blue-deep); }
.dashboard-page .kpi-card .card-body { padding: .6rem .95rem .3rem; }
.dashboard-page .kpi-card .kpi-figure { font-size: 1.5rem; margin-top: .1rem !important; line-height: 1; }
.dashboard-page .card-header { padding-top: .8rem; padding-bottom: .8rem; }

/* KPI count-up skeleton + emerald liveness pulse */
.dashboard-page .kpi-skeleton {
    display: inline-block; width: 58px; height: 1.4rem; border-radius: 6px; vertical-align: -2px;
    background: linear-gradient(90deg, var(--brand-line-soft) 25%, #F3F3F0 37%, var(--brand-line-soft) 63%);
    background-size: 400% 100%; animation: kpiShimmer 1.3s ease-in-out infinite;
}
.dashboard-page .live-badge {
    display: inline-flex; align-items: center; gap: 7px; font-size: .7rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: .07em; color: var(--brand-muted);
}
.dashboard-page .live-dot { position: relative; width: 8px; height: 8px; border-radius: 50%; background: var(--brand-positive); }
.dashboard-page .live-dot::after {
    content: ''; position: absolute; inset: 0; border-radius: 50%; background: var(--brand-positive);
    animation: livePulse 1.9s ease-out infinite;
}
@keyframes kpiShimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }
@keyframes livePulse { 0% { transform: scale(1); opacity: .55; } 100% { transform: scale(3.2); opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
    .dashboard-page .kpi-skeleton,
    .dashboard-page .live-dot::after { animation: none; }
}

/* Tarjetas de gráficas: alto fijo del lienzo y centrado del estado vacío. */
.dashboard-page .chart-box { position: relative; height: 230px; }
.dashboard-page .state-container { min-height: 240px; display: flex; flex-direction: column; align-items: center; justify-content: center; }

/* chart-card metric caption (top-right count + label) */
.dashboard-page .metric-count { font-size: 1.15rem; line-height: 1; }
.dashboard-page .metric-cap { font-size: .625rem; letter-spacing: .08em; color: var(--brand-muted); }

/* Contenedor del mapa: alto mínimo para que Leaflet tenga dónde pintar. */
.dashboard-page .map-shell { min-height: 440px; }

/* map search + state filter overlay (hidden per request; styled if re-enabled) */
.dashboard-page .filter-group {
    display: none !important;
    font-family: var(--brand-font-sans); font-size: 12px; font-weight: 600;
    position: absolute; top: 16px; right: 16px; z-index: 1000; border-radius: 8px; width: 160px;
    background: var(--brand-white); box-shadow: 0 4px 15px rgba(0, 0, 0, .15); overflow: hidden;
}
.dashboard-page .filter-group input[type='checkbox'] { display: none; }
.dashboard-page .filter-group input[type='checkbox'] + label {
    display: block; cursor: pointer; padding: 8px 12px; border-bottom: 1px solid var(--brand-line-soft);
    margin: 0; text-transform: capitalize; color: var(--brand-body); transition: background-color .2s;
}
.dashboard-page .filter-group label:last-of-type { border: none; }
.dashboard-page .filter-group input[type='checkbox']:checked + label { background: var(--brand-navy-tint); color: var(--brand-navy); font-weight: 700; }
.dashboard-page .filter-group input[type='checkbox']:checked + label:before { content: '\2713'; margin-right: 8px; }

/* ===========================================================================
   TAG DETAIL — /tags/{serial}. The reference entity-detail layout; scoped under
   .tag-detail-page. Moved here from inline style attrs (DRY).
   =========================================================================== */
.tag-detail-page .col-icon { width: 45px; }
.tag-detail-page .cell-desc { max-width: 300px; }

/* --- Smart fields: tarjeta de solo lectura (pestaña consulta, no edición) --- */
.tag-detail-page .sfv-read-card {
    padding: 0.75rem 1rem;
    border: 1px solid var(--brand-line);
    border-radius: var(--bs-border-radius, 0.375rem);
    background: var(--brand-paper);
    height: 100%;
}

.tag-detail-page .sfv-read-label {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--brand-muted);
    margin-bottom: 0.25rem;
}

.tag-detail-page .sfv-read-value {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.5rem;
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--brand-ink);
    word-break: break-word;
}

/* Ayuda fija de smartfield (p.ej. tipo Numerico). El JS la inserta DESPUES de la fila del campo, asi
   queda en su propia linea debajo del input (nunca apretada junto al boton de la fila). */
.sfv-hint {
    margin-top: 0.35rem;
    font-size: 0.75rem;
    color: var(--brand-muted);
}

.sfv-error {
    margin-top: 0.35rem;
    font-size: 0.75rem;
}

/* =====================================================================
   PHOSPHOR DUOTONE — arreglo de la fuente (bloqueante para TODOS los iconos)
   La hoja del set (fonts/phosphor/duotone/style.css, importada por el tema)
   trae un bug: fija font-family "Poppins" con !important, asi que ningun
   glifo se renderiza. Aqui se devuelve a la fuente real. custom.css carga
   de ultimo, y con !important gana el empate.
   Cada icono se dibuja en dos capas (:before al 20% + :after encima), que
   es justo el efecto duotone; no hace falta marcado extra.
   ===================================================================== */
.ph-duotone {
    font-family: "Phosphor-Duotone" !important;
}

/* Cuanto relleno lleva un icono.
   El set dibuja cada icono en DOS capas: :before es la silueta rellena y :after el trazo encima.
   La hoja del set fija el relleno en 0.2, que es el aspecto "duotono" de fabrica y se lee cargado
   al lado de una tipografia fina. Aqui se baja a un apunte de color: se nota la profundidad y el
   icono sigue leyendose como un trazo.
   Para cambiar el aspecto de TODA la aplicacion basta este numero:
     0     trazo puro, sin relleno
     0.08  el valor de la casa, un apunte
     0.2   el duotono de fabrica
   Ojo: el relleno se apaga con opacidad, nunca con display:none. La capa :before ocupa su ancho y
   el trazo se le monta encima con un margen negativo; si se quita del flujo, los iconos se parten. */
:root {
    --brand-icon-fill: 0.08;
}

/* !important por lo mismo que la familia de arriba: la hoja del set fija el relleno por icono con
   DOS clases (.ph-duotone.ph-nombre:before), que gana en especificidad a una sola clase por muy
   tarde que cargue este fichero. */
.ph-duotone:before {
    opacity: var(--brand-icon-fill) !important;
}

/* ===================== SELECT2: acentos de marca sobre el tema oficial bootstrap-5 =====================
   El tema bootstrap-5 aporta la estructura (CDN online / self-hosted air-gapped, ver common_header). Aqui
   solo re-tenimos sus azules de Bootstrap (#0d6efd / #86b7fe) a la paleta Accendo y damos una sombra suave
   al menu. Se igualan los selectores del tema en especificidad; custom.css carga despues y gana el empate. */

/* Foco/abierto: borde y anillo de marca */
.select2-container--bootstrap-5.select2-container--focus .select2-selection,
.select2-container--bootstrap-5.select2-container--open .select2-selection {
    border-color: var(--brand-blue-deep);
    box-shadow: 0 0 0 0.25rem rgba(var(--brand-blue-deep-rgb), 0.25);
}

/* Menu como tarjeta flotante bien definida: borde completo del MISMO tono que el control (coherencia),
   esquinas redondeadas, ligera separacion y sombra suave. El tema bootstrap-5 "pega" el menu al control
   quitando el borde superior (variantes --below/--above); lo restauramos para que sea una tarjeta y no un
   bloque blanco sin borde. overflow:hidden recorta el resaltado de opciones a las esquinas redondeadas. */
.select2-container--bootstrap-5 .select2-dropdown,
.select2-container--bootstrap-5 .select2-dropdown--below,
.select2-container--bootstrap-5 .select2-dropdown--above {
    border: 1px solid var(--brand-line);
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 12px 32px -12px rgba(var(--brand-navy-rgb), 0.30), 0 2px 8px rgba(var(--brand-navy-rgb), 0.06);
}

.select2-container--bootstrap-5 .select2-dropdown--below { margin-top: 4px; }
.select2-container--bootstrap-5 .select2-dropdown--above { margin-bottom: 4px; }

/* Opcion resaltada (hover/teclado): tono de marca calmado + texto navy (en vez del gris del tema) */
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option.select2-results__option--highlighted {
    color: var(--brand-navy);
    background-color: rgba(var(--brand-blue-deep-rgb), 0.12);
}

/* Opcion seleccionada: navy de marca en vez del azul de Bootstrap */
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option.select2-results__option--selected,
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[aria-selected=true]:not(.select2-results__option--highlighted) {
    color: #fff;
    background-color: var(--brand-navy);
}

/* Campo de busqueda del menu: integrado en la tarjeta (borde sutil coherente con el menu + mismo radio).
   Antes su borde resaltaba sobre un menu casi sin borde; ahora ambos comparten el mismo lenguaje visual. */
.select2-container--bootstrap-5 .select2-search--dropdown {
    padding: 8px 8px 4px;
}

.select2-container--bootstrap-5 .select2-search--dropdown .select2-search__field {
    border: 1px solid var(--brand-line);
    border-radius: 8px;
    padding: 7px 10px;
}

.select2-container--bootstrap-5 .select2-dropdown .select2-search .select2-search__field:focus {
    border-color: var(--brand-blue-deep);
    box-shadow: 0 0 0 0.2rem rgba(var(--brand-blue-deep-rgb), 0.18);
}

/* Altura de la seleccion multiple: el tema pone el buscador en linea a ancho completo, asi que la
   primera etiqueta elegida lo empuja a un segundo renglon y la caja crece al doble. Se deja que el
   contenido fluya y que el buscador ocupe solo el hueco restante: una fila, altura de campo normal. */
.select2-container--bootstrap-5 .select2-selection--multiple {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem;
}

.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered {
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem;
}

.select2-container--bootstrap-5 .select2-selection--multiple .select2-search--inline {
    flex: 1 1 4em;
    width: auto;
    min-width: 4em;
}

/* Chips (seleccion multiple): tono de marca */
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice {
    margin: 0;
    padding-top: .05rem;
    padding-bottom: .05rem;
    color: var(--brand-navy);
    background-color: rgba(var(--brand-blue-deep-rgb), 0.10);
    border-color: rgba(var(--brand-blue-deep-rgb), 0.20);
}

/* --- Paginación client-side (jquery.simplePagination) alineada al patrón Bootstrap 5 de la plantilla.
   El plugin no emite .page-item ni .page-link en la página actual (un span.current), por eso las reglas
   propias; valen en todas las páginas de detalle, no solo en la de tag. --- */
.pagination.simple-pagination {
    display: flex;
    flex-wrap: wrap;
    padding-left: 0;
    gap: 0;
}

.pagination.simple-pagination > li {
    list-style: none;
}

.pagination.simple-pagination > li > a.page-link,
.pagination.simple-pagination > li > span.current,
.pagination.simple-pagination > li > span.ellipse {
    position: relative;
    display: block;
    min-width: 2rem;
    padding: 0.25rem 0.5rem;
    font-size: 0.875rem;
    line-height: 1.25;
    text-align: center;
    color: var(--brand-blue-deep);
    background-color: var(--brand-white);
    border: 1px solid var(--brand-line);
    text-decoration: none;
    margin-left: -1px;
}

.pagination.simple-pagination > li:first-child > a.page-link,
.pagination.simple-pagination > li:first-child > span.current,
.pagination.simple-pagination > li:first-child > span.ellipse {
    margin-left: 0;
    border-top-left-radius: var(--bs-pagination-border-radius, 0.375rem);
    border-bottom-left-radius: var(--bs-pagination-border-radius, 0.375rem);
}

.pagination.simple-pagination > li:last-child > a.page-link,
.pagination.simple-pagination > li:last-child > span.current,
.pagination.simple-pagination > li:last-child > span.ellipse {
    border-top-right-radius: var(--bs-pagination-border-radius, 0.375rem);
    border-bottom-right-radius: var(--bs-pagination-border-radius, 0.375rem);
}

.pagination.simple-pagination > li > a.page-link:hover {
    z-index: 2;
    color: var(--brand-navy);
    background-color: var(--brand-paper);
    border-color: var(--brand-line);
}

.pagination.simple-pagination > li.active > span.current {
    z-index: 3;
    color: var(--brand-white);
    background-color: var(--brand-navy);
    border-color: var(--brand-navy);
}

.pagination.simple-pagination > li.disabled > span.current,
.pagination.simple-pagination > li.disabled > span.ellipse {
    color: var(--brand-muted);
    background-color: var(--brand-white);
    border-color: var(--brand-line);
    pointer-events: none;
}

/* --- QR del EPC (Reports, QR code of the tag page); el QR hereda el navy de marca --- */
.tag-qr {
    width: 100%;
    max-width: 196px;
    aspect-ratio: 1 / 1;
    padding: 14px;
    border: 1px solid var(--brand-line);
    border-radius: 14px;
    background: #fff;
    box-shadow: inset 0 0 0 1px rgba(21, 36, 60, .015);
}
.tag-qr svg { display: block; width: 100%; height: 100%; }
/* el fondo blanco del SVG se mantiene; sólo recoloreamos los módulos al navy de marca */
.tag-qr svg path { fill: var(--brand-navy); }
.tag-qr-epc {
    font-family: var(--brand-font-mono);
    font-size: .8rem;
    letter-spacing: .02em;
    color: var(--brand-ink);
    word-break: break-all;
    user-select: all;
}
.tag-qr-copy.is-copied { color: var(--brand-success); border-color: currentColor; }


/* ===========================================================================
   SNAPSHOTS — /analytics/snapshots (KPI cards + airy grouped table)
   =========================================================================== */
.snap-asof { font-size: .78rem; color: var(--brand-body); }
.snap-asof b { color: var(--brand-navy); font-weight: 600; }

/* KPI cards */
.kpi { transition: transform .2s ease, box-shadow .2s ease; }
/* .kpi cards are fully static — no hover lift, no hover shadow (brand: no hover movement) */
.kpi .card-body { padding: .7rem .95rem .5rem; }
.kpi .lbl { font-size: .625rem; text-transform: uppercase; letter-spacing: .08em; color: var(--brand-muted); font-weight: 600; }
.kpi .fig { font-family: var(--brand-font-mono); font-size: 1.55rem; font-size: clamp(1.15rem, 5.5vw, 1.55rem); font-weight: 600; color: var(--brand-navy); line-height: 1; font-variant-numeric: tabular-nums; }

/* trend chip. */
.trend { display: inline-flex; align-items: center; gap: 1px; font-size: .66rem; font-weight: 700; padding: 1px 5px; border-radius: 999px; }
.trend.up { color: var(--brand-positive-text); background: rgba(var(--brand-positive-rgb), .12); }
.trend.down { color: var(--brand-warning, #B5852A); background: rgba(181, 133, 42, .12); }
.trend.flat { color: var(--brand-muted); background: var(--brand-line-soft); }

/* info icon + Bootstrap tooltip (brand-styled, subtle light). Every Bootstrap tooltip in the app gets
   this style (js/common/tooltips.js initializes them all); .snap-tip is kept for the markup that names it. */
.info { color: #C9C9C2; cursor: help; font-size: .82rem; line-height: 1; vertical-align: middle; }
.info:hover { color: var(--brand-blue-deep); }
/* On a touch screen the icon is a tap target (js/common/tooltips.js), not a hover hint. */
@media (hover: none) { .info { cursor: pointer; } }
.info:focus-visible { color: var(--brand-blue-deep); outline: 2px solid var(--brand-blue); outline-offset: 2px; border-radius: 3px; }
th .info { font-size: .74rem; margin-left: 3px; }
/* The icon is about 13px wide, too small to hold the pointer steady. Padding widens what answers the pointer
   and an equal negative margin keeps the layout where it was; position paints the padding above the text that
   follows the icon. A pseudo-element cannot do this: the icon font draws its glyph on ::before and ::after. */
i.info { --info-hit: .35rem; position: relative; padding: var(--info-hit); margin: calc(-1 * var(--info-hit)); }
th i.info { margin-left: calc(3px - var(--info-hit)); }
i.info:focus-visible { outline-offset: calc(2px - var(--info-hit)); }
/* A tip never receives the pointer, so one that opens under it cannot fire a mouse-out on its host. */
.tooltip { pointer-events: none; }
.tooltip, .snap-tip { --bs-tooltip-bg: #fff; --bs-tooltip-color: var(--brand-body); --bs-tooltip-opacity: 1; }
.tooltip .tooltip-inner, .snap-tip .tooltip-inner { max-width: 300px; text-align: left; white-space: pre-line; font-family: var(--brand-font-sans); font-size: .7rem; font-weight: 400;
    line-height: 1.5; padding: .55rem .7rem; border: 1px solid var(--brand-line); border-radius: 10px; box-shadow: 0 12px 30px rgba(14, 42, 69, .14); }

/* grouped, pinned header — scoped to the snapshots table only.
   --grp-h locks the group-row height to the cols-row sticky offset so the rowspan'd
   first/last columns share the SAME bottom line as the middle columns (no 1px drift). */
#snapshotsTable thead { --grp-h: 30px; }
#snapshotsTable thead th { position: sticky; background: #fff; z-index: 5; }
#snapshotsTable thead tr.grp th { top: 0; height: var(--grp-h); box-sizing: border-box;
    border: none; font-size: .6rem; text-transform: uppercase; letter-spacing: .1em; font-weight: 700;
    text-align: center; padding: .45rem .5rem .2rem; color: var(--brand-muted); }
#snapshotsTable thead tr.cols th { top: var(--grp-h); }
/* one continuous dark line under the WHOLE header — a real border (not a box-shadow, which gets
   clipped on the sticky FECHA/DETALLE cells), so the pinned columns get the exact same line. */
#snapshotsTable thead tr.cols th,
#snapshotsTable thead tr.grp th.col-pin-left,
#snapshotsTable thead tr.grp th.col-pin-right { border-bottom: 2px solid var(--brand-ink) !important; box-shadow: none !important; }
#snapshotsTable thead tr.grp th.g-stock { color: var(--brand-blue-deep); }
#snapshotsTable thead tr.grp th.g-flow { color: var(--brand-positive-text); }
#snapshotsTable thead tr.grp th.g-delta { color: var(--brand-navy); }
#snapshotsTable thead tr.grp th.col-pin-left, #snapshotsTable thead tr.grp th.col-pin-right { z-index: 8; will-change: transform; }

/* delta cell */
#snapshotsTable td .d { font-size: .72rem; font-weight: 700; font-variant-numeric: tabular-nums; }
#snapshotsTable td .d.up { color: var(--brand-positive-text); }
#snapshotsTable td .d.down { color: var(--brand-warning, #B5852A); }
#snapshotsTable td .d.flat { color: var(--brand-muted); font-weight: 400; }

/* Mirror, not manager: on the analytics & snapshot pages a rise/fall in a neutral stock/flow
   figure (Tags, Inventory, Lent, Movements, Returns, Errores…) is NOT good/bad. So the delta
   chips drop the emerald-up / amber-down verdict and render calm — direction is carried by the
   ▲/▼ glyph and the sign only. The base .trend/.d/.norm-dev rules stay intact for surfaces where
   the customer DID define a good direction (e.g. the dashboard). Higher specificity → wins. */
.analytics-overview-page .trend.up, .analytics-overview-page .trend.down,
.snapshot-detail-page .trend.up,    .snapshot-detail-page .trend.down,
.snapshots-page .trend.up,          .snapshots-page .trend.down,
.ai-insights-page .trend.up,        .ai-insights-page .trend.down {
    color: var(--brand-navy);
    background: var(--brand-line-soft);
}
.snapshots-page #snapshotsTable td .d.up,
.snapshots-page #snapshotsTable td .d.down {
    color: var(--brand-navy);
}
.snapshot-detail-page .norm-dev.up, .snapshot-detail-page .norm-dev.down,
.ai-insights-page .norm-dev.up,     .ai-insights-page .norm-dev.down {
    color: var(--brand-navy);
}

/* snapshot detail view */
.detail-stats { row-gap: .5rem; }
.detail-stat { text-align: center; padding: .45rem .25rem; border-radius: 8px; background: var(--brand-paper); height: 100%; }
.detail-stat .fig { font-family: var(--brand-font-mono); font-size: 1.1rem; font-weight: 600; color: var(--brand-navy); line-height: 1.1; }
.detail-stat .lbl { font-size: .56rem; text-transform: uppercase; letter-spacing: .05em; color: var(--brand-muted); font-weight: 600; margin-top: 2px; }
.detail-churn { font-size: .82rem; }
.detail-churn th { font-size: .6rem; text-transform: uppercase; letter-spacing: .06em; color: var(--brand-muted); font-weight: 600; border-bottom: 1px solid var(--brand-line-soft); }
.detail-churn td { color: var(--brand-body); border-bottom: 1px solid var(--brand-line-soft); }
/* per-stat variation chip sits just under the figure; churn cells get a small inline gap */
.detail-stat .stat-delta { margin-top: 3px; min-height: 1rem; line-height: 1; }
.detail-stat .stat-delta .trend { font-size: .58rem; padding: 0 4px; }
.detail-churn td .trend { font-size: .58rem; padding: 0 4px; }
/* number stays left, variation chip pushed to the cell's right edge */
.detail-churn .churn-cell { display: flex; align-items: center; justify-content: space-between; gap: .4rem; }

/* comparison caption under the detail title */
.snap-compare { font-size: .72rem; color: var(--brand-muted); margin-top: .15rem; }
/* day-to-day navigation: labelled previous/next buttons split left/right */
.snap-daynav { gap: .5rem; }
.daynav-btn { display: inline-flex; align-items: center; gap: .4rem; font-size: .82rem; font-weight: 600;
    color: var(--brand-blue-deep); text-decoration: none; padding: .4rem .8rem;
    border: 1px solid var(--brand-line); border-radius: 8px; }
.daynav-btn:hover { background: var(--brand-paper); color: var(--brand-navy); }
.snap-asof .d-block { font-size: .68rem; color: var(--brand-muted); margin-top: 1px; }

/* ApexCharts canvas in the detail cards */
.snap-chart { margin-top: .25rem; min-height: 280px; }

/* deployment-age ladder — distribution of currently-deployed (LENT) assets by time since last scan */
.age-ladder { display: flex; flex-direction: column; gap: .3rem; margin-top: .45rem; }
.age-row { display: grid; grid-template-columns: 4.6rem 1fr 3rem; align-items: center; gap: .55rem; }
.age-lbl { font-size: .62rem; text-transform: uppercase; letter-spacing: .03em; color: var(--brand-muted); font-weight: 600; }
.age-bar { height: 8px; border-radius: 4px; background: var(--brand-paper); overflow: hidden; }
.age-fill { display: block; height: 100%; border-radius: 4px; background: var(--brand-blue-deep); min-width: 2px; transition: width .3s ease; }
.age-val { font-family: var(--brand-font-mono); font-size: .78rem; color: var(--brand-navy); text-align: right; }

/* "today vs your own 30-day norm" strip — the non-obvious context (flow card) */
.norm-strip { background: var(--brand-paper); border: 1px solid var(--brand-line-soft); border-radius: 10px; padding: .55rem .7rem; margin-bottom: .85rem; }
.norm-title { display: block; margin-bottom: .4rem; }
.norm-cells { display: grid; grid-template-columns: repeat(2, 1fr); gap: .5rem .9rem; }
@media (min-width: 1200px) { .norm-cells { grid-template-columns: repeat(4, 1fr); } }
.norm-cell { display: flex; flex-direction: column; line-height: 1.25; }
.norm-lbl { font-size: .56rem; text-transform: uppercase; letter-spacing: .05em; color: var(--brand-muted); font-weight: 600; }
.norm-dev { font-family: var(--brand-font-mono); font-size: .98rem; font-weight: 600; }
.norm-dev.up { color: var(--brand-positive-text); }
.norm-dev.down { color: var(--brand-warning, #B5852A); }
.norm-dev.flat { color: var(--brand-muted); }
.norm-base { font-size: .6rem; color: var(--brand-muted); }

/* analytics — time-range switch (segmented, subtle) */
.range-switch { display: inline-flex; border: 1px solid var(--brand-line); border-radius: 8px; overflow: hidden; }
.range-switch a { white-space: nowrap; font-size: .72rem; font-weight: 600; color: var(--brand-muted); text-decoration: none;
    padding: .25rem .65rem; border-left: 1px solid var(--brand-line); }
.range-switch a:first-child { border-left: 0; }
.range-switch a:hover { background: var(--brand-paper); color: var(--brand-navy); }
.range-switch a.is-active { background: var(--brand-navy); color: #fff; }

/* analytics — "based on snapshots …" provenance note (muted, one line, never per-number) */
.prov-note { font-size: .68rem; color: var(--brand-muted); }
.prov-note b { color: var(--brand-navy); font-weight: 600; }

/* page intro — plain-language explainer callout at the top of a view (self-service clarity) */
.page-intro { display: flex; align-items: flex-start; gap: .6rem; background: var(--brand-paper);
    border: 1px solid var(--brand-line-soft); border-radius: 10px; padding: .65rem .85rem;
    margin-bottom: 1rem; font-size: .82rem; color: var(--brand-body); line-height: 1.5; }
.page-intro i { color: var(--brand-blue-deep); font-size: 1rem; flex: 0 0 auto; margin-top: 1px; }

/* =====================================================================
   AI INSIGHTS page (/analytics/ai) — reuses .ai-spark/.insight-brief/.hl/
   .ai-meta/.ai-foot a from the dashboard panel; only the page-specific bits here.
   ===================================================================== */
.ai-card .ai-model { font-family: var(--brand-font-mono); font-size: .58rem; color: var(--brand-muted); text-transform: uppercase; letter-spacing: .04em; }
.ai-highlights { display: flex; flex-direction: column; gap: .35rem; margin: .6rem 0; }
.ai-highlights .hl { padding: .45rem .65rem; border-radius: 8px; background: var(--brand-paper);
    border: 1px solid var(--brand-line-soft); align-items: flex-start; font-size: .82rem; line-height: 1.4; }
/* dot centered on the FIRST text line regardless of type or how many lines the chip wraps to:
   line box = 1.4em, dot = 8px, so centre it at (1.4em - 8px)/2 from the top. */
.ai-highlights .hl .dot { align-self: flex-start; margin-top: calc((1.4em - 8px) / 2); }
.ai-highlights .hl span:last-child { color: var(--brand-navy); }
.ai-report { font-size: .9rem; color: var(--brand-body); line-height: 1.6; margin-top: .75rem; }
.ai-report h1, .ai-report h2 { font-size: 1rem; font-weight: 700; color: var(--brand-navy); margin: 1rem 0 .4rem; }
.ai-report h3 { font-size: .9rem; font-weight: 700; color: var(--brand-navy); margin: .8rem 0 .3rem; }
.ai-report ul, .ai-report ol { padding-left: 1.15rem; margin: .3rem 0; }
.ai-report li { margin: .15rem 0; }
.ai-report blockquote { border-left: 3px solid var(--brand-blue-deep); background: var(--brand-paper); padding: .5rem .8rem; margin: .6rem 0; border-radius: 0 6px 6px 0; color: var(--brand-navy); }
.ai-report strong { color: var(--brand-navy); }
.ai-foot { border-top: 1px solid var(--brand-line-soft); }
.ai-foot b { color: var(--brand-navy); font-weight: 600; }
.ai-disclaimer { font-size: .68rem; color: var(--brand-muted); font-style: italic; line-height: 1.4; }

/* staged generation checklist */
.ai-spinner { width: 16px; height: 16px; border: 2px solid var(--brand-line); border-top-color: var(--brand-blue-deep); border-radius: 50%; animation: ai-spin .8s linear infinite; flex: 0 0 auto; }
@keyframes ai-spin { to { transform: rotate(360deg); } }
.ai-steps { list-style: none; margin: 0; padding: 0; }
.ai-steps .ai-step { display: flex; align-items: center; gap: .5rem; padding: .35rem 0; font-size: .82rem; color: var(--brand-muted); opacity: .5; transition: opacity .25s ease, color .25s ease; }
.ai-steps .ai-step i { color: var(--brand-line); font-size: .9rem; }
.ai-steps .ai-step.is-active { opacity: 1; color: var(--brand-body); }
.ai-steps .ai-step.is-active i { color: var(--brand-positive); }

/* snapshot-truth header + date-jump */
.ai-datejump { max-width: 210px; }
/* iOS gives a date input an intrinsic width wider than its group, which pushed it over Exportar. */
.ai-datejump .form-control { min-width: 0; width: 1%; }
.ai-truth .detail-stat { background: var(--brand-paper); }

/* "Insights" badge on the AI card (calm — not a live indicator) */
.ai-badge { display: inline-flex; align-items: center; gap: 6px; font-size: .6rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: .08em; color: var(--brand-blue-deep); }
.ai-badge-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--brand-blue-deep); flex: 0 0 auto; }

/* recent-days scrub strip under the two columns */
.ai-recent { display: flex; align-items: center; flex-wrap: wrap; gap: .35rem; padding: .25rem 0; }
.ai-recent-chip { font-family: var(--brand-font-mono); font-size: .72rem; color: var(--brand-blue-deep);
    text-decoration: none; padding: .2rem .55rem; border-radius: 6px; border: 1px solid var(--brand-line-soft); }
.ai-recent-chip:hover { background: var(--brand-paper); }
.ai-recent-chip.is-current { background: var(--brand-navy-tint); border-color: var(--brand-navy-tint-border);
    color: var(--brand-navy); font-weight: 600; }

/* history list */
.ai-hist-item { display: block; padding: .5rem .6rem; border-radius: 8px; text-decoration: none; border: 1px solid transparent; margin-bottom: .25rem; }
.ai-hist-item:hover { background: var(--brand-paper); }
.ai-hist-item.is-current { border-color: var(--brand-navy-tint-border); background: var(--brand-navy-tint); }
.ai-hist-item .num { font-family: var(--brand-font-mono); font-size: .72rem; color: var(--brand-blue-deep); font-weight: 600; }
.ai-hist-item .brief { font-size: .78rem; color: var(--brand-body); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* =====================================================================
   Login — split-panel (navy brand left / white form right).
   Layout + skin only; reuses brand tokens and Bootstrap form/button.
   ===================================================================== */
.login-split {
    display: flex;
    min-height: 100vh;
    min-height: 100dvh;
    width: 100%;
    /* soft light from upper-centre: almost white at top, cielo blue elsewhere */
    background: radial-gradient(ellipse at 50% 0%, #FFFFFF 0%, #F4F7FB 60%);
}

/* ----- Left: navy brand panel ----- */
.login-brand {
    position: relative;
    flex: 0 0 46%;
    max-width: 46%;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: 52px;
    /* Accendo hero depth: a blue glow top-right + a deep-blue glow bottom-left over
       the navy — reads "blue to blue" across the panel; the wave field sits over it. */
    background:
        radial-gradient(120% 62% at 85% 6%, rgba(73, 141, 190, .32) 0%, rgba(21, 36, 60, 0) 60%),
        radial-gradient(115% 58% at 8% 100%, rgba(44, 90, 130, .42) 0%, rgba(21, 36, 60, 0) 56%),
        linear-gradient(160deg, #1B2B48 0%, #15243C 52%, #0E1A2C 100%);
    color: var(--brand-white);
    overflow: hidden;
}

.login-brand > *:not(.login-wave-field) { position: relative; z-index: 1; }
/* Onda de firma de Accendo: el haz de lineas tranquilo de osciloscopio,
   dibujado en un <canvas> animado (sin dependencias ni CDN, apto sin conexion;
   con prefers-reduced-motion pinta un solo cuadro fijo y se pausa fuera de pantalla).
   Va DETRAS del contenido del panel. */
.login-wave-field {
    position: absolute; inset: 0; z-index: 0;
    width: 100%; height: 100%; pointer-events: none;
}

.login-brand__logo { height: 68px; width: auto; }

/* whiten the (emerald) Trackvy + dark Accendo marks — no white asset exists */
.login-brand__logo--white,
.login-brand__accendo { filter: brightness(0) invert(1); }

/* eyebrow/label look — Geist sans (no mono), uppercase, tracked */
.login-eyebrow,
.login-label {
    font-family: var(--brand-font-sans);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--brand-body); /* muted no alcanza AA a 11 px sobre blanco */
}

.login-brand__eyebrow {
    color: rgba(255, 255, 255, .5);
    margin-bottom: 18px;
}

.login-brand__headline {
    font-family: var(--brand-font-sans);
    font-weight: 600;
    font-size: 40px;
    line-height: 1.18;
    letter-spacing: -0.02em;
    margin: 0 0 18px;
    max-width: 18ch;
    /* near-white, but a touch below pure white so the Trackvy + Accendo logos stay the true whites */
    color: rgba(255, 255, 255, .92);
}

/* second headline line — own line, light-blue accent (Accendo hero accent on navy) */
.login-brand__accent { display: block; color: var(--brand-blue-bright); font-weight: 700; }

.login-brand__support {
    color: rgba(255, 255, 255, .75);
    font-size: 17px;
    line-height: 1.6;
    margin: 0;
    max-width: 42ch;
}

.login-brand__footer {
    display: flex;
    align-items: center;
    gap: 10px;
    color: rgba(255, 255, 255, .40);
    font-size: 13px;
}

.login-brand__accendo { height: 25px; width: auto; opacity: .9; }
/* small Trackvy icon replacing the form eyebrow (≈ width of "Ingre") */

/* ----- Right: white form panel ----- */
.login-form {
    position: relative;
    flex: 1 1 54%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 48px 32px;
}

/* Enlaces legales al pie del panel blanco, abajo a la derecha; en pantalla angosta pasan al
   flujo, centrados (ver abajo) */
.login-legal {
    position: absolute;
    right: 32px;
    bottom: 16px;
    display: flex;
    justify-content: flex-end;
    flex-wrap: wrap;
    column-gap: 20px;
    row-gap: 4px;
    font-size: 12px;
}
.login-legal a { color: var(--brand-body); text-decoration: none; }
.login-legal a:hover { color: var(--brand-navy); text-decoration: underline; }

/* language switcher — a small segmented pill (globe + EN/ES), centered under the support link */
.login-lang {
    display: flex;
    align-items: center;
    gap: 4px;
    width: fit-content;
    margin: 18px auto 0;
    padding: 3px 5px 3px 9px;
    border: 1px solid var(--brand-line);
    border-radius: 999px;
    font-family: var(--brand-font-sans);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
}
.login-lang__icon { color: var(--brand-muted); font-size: 0.9rem; line-height: 1; }
.login-lang a {
    color: var(--brand-muted);
    text-decoration: none;
    padding: 3px 10px;
    border-radius: 999px;
    line-height: 1;
    transition: background-color .15s ease, color .15s ease;
}
.login-lang a:hover { color: var(--brand-navy); }
.login-lang a.is-active { color: var(--brand-navy); background: var(--brand-navy-tint); }

.login-form__inner { width: 100%; max-width: 380px; }

/* mobile-only brand marks (shown when the navy panel is hidden) — see media query below */
.login-form__logo,
.login-form__footer { display: none; }

.login-form__eyebrow { display: block; margin-bottom: 10px; }

.login-form__title {
    font-family: var(--brand-font-sans);
    font-weight: 600;
    font-size: 28px;
    letter-spacing: -0.02em;
    color: var(--brand-ink);
    margin: 0 0 6px;
}

.login-form__subtitle {
    color: var(--brand-muted);
    font-size: 14px;
    margin: 0 0 28px;
}

.login-label { display: block; margin-bottom: 7px; }

.login-field { margin-bottom: 18px; }

.login-split .form-control {
    border-color: var(--brand-line);
    border-radius: 7px;
    padding: 10px 12px;
    height: auto;
}

/* input with a leading icon + inline validation (email check / error states) */
.login-input { position: relative; }
.login-input .form-control { padding-left: 38px; }
.login-input__lead {
    position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
    color: var(--brand-muted); font-size: 1.05rem; line-height: 1; pointer-events: none;
}
.login-input:focus-within .login-input__lead { color: var(--brand-blue-deep); }
.login-field:focus-within .login-label { color: var(--brand-navy); }
.login-input__ok {
    position: absolute; right: 12px; top: 50%; transform: translateY(-50%) scale(.7);
    opacity: 0; color: var(--brand-positive, #2F7A5B);
    transition: opacity .2s ease, transform .2s ease; pointer-events: none;
}
.login-input.is-valid .login-input__ok { opacity: 1; transform: translateY(-50%) scale(1); }
.login-input.is-valid .form-control { padding-right: 38px; }
.login-input.is-error .form-control { border-color: var(--brand-danger, #A6402F); }
.login-input.is-error .form-control:focus { box-shadow: 0 0 0 3px rgba(166, 64, 47, .14); }

/* small hint line under a field (email error / caps-lock) */
.login-hint { margin: 6px 0 0; font-size: .78rem; display: flex; align-items: center; gap: 6px; color: var(--brand-muted); }
.login-hint[hidden] { display: none; }
.login-hint > i { font-size: 15px; line-height: 1; flex: 0 0 auto; }
.login-hint--error { color: var(--brand-danger, #A6402F); }
.login-caps { color: var(--brand-blue-deep); }

/* password show/hide eye toggle */
.login-password { position: relative; }
.login-password .form-control { padding-right: 42px; }

.login-eye {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    background: transparent;
    color: var(--brand-muted);
    cursor: pointer;
}
.login-eye:hover { color: var(--brand-blue-deep); }

/* primary action — navy fill, white text (matches the app's primary button) */
.login-submit {
    gap: 8px;
    padding: 11px 16px;
    font-weight: 600;
    margin-top: 4px;
    --bs-btn-bg: var(--brand-navy);
    --bs-btn-border-color: var(--brand-navy);
    --bs-btn-color: #ffffff;
    --bs-btn-hover-bg: var(--brand-navy-600);
    --bs-btn-hover-border-color: var(--brand-navy-600);
    --bs-btn-hover-color: #ffffff;
    --bs-btn-active-bg: var(--brand-navy-700);
    --bs-btn-active-border-color: var(--brand-navy-700);
    --bs-btn-active-color: #ffffff;
}

/* submit: idle label swaps to a spinner while authenticating (JS toggles .is-loading) */
.login-submit__idle,
.login-submit__busy { display: inline-flex; align-items: center; gap: 8px; }
.login-submit__busy { display: none; }
.login-submit.is-loading { pointer-events: none; }
.login-submit.is-loading .login-submit__idle { display: none; }
.login-submit.is-loading .login-submit__busy { display: inline-flex; }
.login-spin { animation: login-spin .7s linear infinite; }
@keyframes login-spin { to { transform: rotate(360deg); } }

.login-support {
    color: var(--brand-body);
    font-size: 13px;
    text-align: center;
    margin: 18px 0 0;
}
.login-support a { color: var(--brand-blue-deep); text-decoration: none; }
.login-support a:hover { color: var(--brand-navy); text-decoration: underline; }

/* pretty error alert (container only — text comes from login JS) */
.login-alert {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    margin-bottom: 22px;
    border: 1px solid var(--brand-danger-tint-border);
    border-radius: 7px;
    background: var(--brand-danger-tint);
    color: var(--brand-danger);
}
.login-alert > i { font-size: 18px; line-height: 1.4; flex: 0 0 auto; }
.login-alert p { font-size: 14px; line-height: 1.4; }
.login-alert .btn-close {
    margin-left: auto;
    padding: 2px;
    font-size: 10px;
    flex: 0 0 auto;
}

/* ----- Responsive: drop the navy panel, leave the plain white form ----- */
@media (max-width: 991.98px) {
    .login-brand { display: none; }
    /* La pantalla entera es el panel: el formulario queda al centro y el pie (Accendo y enlaces legales) se fija
       abajo. El relleno de arriba y de abajo es el mismo para que el centro no se corra. */
    .login-form { flex: 1 1 100%; min-height: 100vh; min-height: 100svh; padding: 112px 24px; }

    /* Sin el panel azul marino, el logo de Trackvy va sobre el formulario, centrado y con mas aire antes de el.
       Usa los recursos de color normal (sin filtro blanco) sobre el panel claro. */
    .login-form__logo { display: block; text-align: center; margin-bottom: 44px; }
    .login-form__logo img { height: 64px; width: auto; }

    /* "Desarrollado por" and the Accendo logo on one line, centered, pinned near the bottom of the screen */
    .login-form__footer {
        position: absolute;
        left: 24px;
        right: 24px;
        bottom: 44px;
        display: flex;
        flex-direction: row;
        justify-content: center;
        align-items: center;
        gap: 8px;
        color: var(--brand-body);
        font-size: 13px;
        text-align: center;
    }
    .login-form__footer img { height: 20px; width: auto; }

    /* sin el panel navy los enlaces legales van al borde inferior de la pantalla, centrados */
    .login-legal { left: 24px; right: 24px; bottom: 14px; justify-content: center; }

    /* Fondo sin el panel navy: un tinte azul tenue detrás del logo que se desvanece antes del formulario. Solo CSS,
       sin imagen ni animación. */
    .login-split {
        background:
            linear-gradient(180deg,
                rgba(var(--brand-blue-deep-rgb), .12) 0,
                rgba(var(--brand-blue-deep-rgb), .05) 170px,
                rgba(var(--brand-blue-deep-rgb), 0) 320px),
            var(--brand-white);
    }
}

/* ── Legal document pages (/terms, /legal/*) ───────────────────────────── */
.legal-page { background: var(--brand-canvas); min-height: 100vh; font-family: var(--brand-font-sans); }

.legal-topbar {
    background: var(--brand-white);
    border-bottom: 1px solid var(--brand-line);
    padding: 1.25rem 2rem;
    display: flex;
    justify-content: center;
    align-items: center;
}
.legal-logo {
    height: 36px;
    width: auto;
}

.legal-body {
    max-width: 780px;
    margin: 2.5rem auto;
    padding: 0 1.5rem 5rem;
}

.legal-meta {
    background: var(--brand-white);
    border: 1px solid var(--brand-line);
    border-radius: var(--border-radius);
    padding: 1rem 1.5rem;
    margin-bottom: 2rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem 2rem;
}
.legal-meta span {
    font-size: 0.8rem;
    color: var(--brand-muted);
}
.legal-meta span strong {
    color: var(--brand-body);
    font-weight: 600;
}

.legal-doc-title {
    font-size: 1.625rem;
    font-weight: 700;
    color: var(--brand-navy);
    line-height: 1.2;
    margin: 0 0 0.375rem;
}
.legal-doc-sub {
    font-size: 0.875rem;
    color: var(--brand-muted);
    margin: 0 0 2rem;
}

.legal-toc {
    background: var(--brand-paper);
    border: 1px solid var(--brand-line);
    border-left: 3px solid var(--brand-navy);
    border-radius: var(--border-radius);
    padding: 1.125rem 1.5rem;
    margin-bottom: 2.5rem;
}
.legal-toc .toc-label {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--brand-muted);
    margin-bottom: 0.75rem;
}
.legal-toc ol {
    margin: 0;
    padding-left: 1.25rem;
    column-count: 2;
    column-gap: 2rem;
}
.legal-toc li { margin-bottom: 0.3rem; }
.legal-toc a {
    font-size: 0.8125rem;
    color: var(--brand-blue-deep);
    text-decoration: none;
}
.legal-toc a:hover { text-decoration: underline; }

.legal-section { margin-bottom: 2.75rem; }
.legal-section h2 {
    font-size: 0.9375rem;
    font-weight: 700;
    color: var(--brand-navy);
    padding-bottom: 0.5rem;
    border-bottom: 1px solid var(--brand-line);
    margin-bottom: 1.125rem;
    display: flex;
    gap: 0.625rem;
    align-items: baseline;
}
.legal-section h2 .num {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--brand-muted);
    flex-shrink: 0;
}
.legal-section p {
    font-size: 0.9375rem;
    line-height: 1.8;
    color: var(--brand-body);
    margin-bottom: 0.875rem;
}
.legal-section ol[type="a"],
.legal-section ul {
    padding-left: 1.5rem;
    margin-bottom: 0.875rem;
}
.legal-section li {
    font-size: 0.9375rem;
    line-height: 1.8;
    color: var(--brand-body);
    margin-bottom: 0.375rem;
}

.non-waivable-banner {
    background: var(--brand-navy);
    color: #fff;
    border-radius: var(--border-radius);
    padding: 1rem 1.25rem 1rem 1.5rem;
    margin-bottom: 1.5rem;
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
}
.non-waivable-banner .nw-icon {
    font-size: 1.25rem;
    line-height: 1;
    color: var(--brand-blue-bright);
    flex-shrink: 0;
    padding-top: 0.1rem;
}
.non-waivable-banner .nw-text {
    font-size: 0.8125rem;
    line-height: 1.55;
    color: rgba(255,255,255,0.9);
}
.non-waivable-banner .nw-text strong {
    display: block;
    color: #fff;
    font-weight: 700;
    margin-bottom: 0.2rem;
    font-size: 0.875rem;
}

.legal-footer-bar {
    margin-top: 3rem;
    padding-top: 1.5rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
}
.legal-footer-bar p {
    font-size: 0.8rem;
    color: var(--brand-muted);
    margin: 0;
}
.legal-footer-bar a { color: var(--brand-blue-deep); text-decoration: none; }
.legal-footer-bar a:hover { text-decoration: underline; }

@media (max-width: 600px) {
    .legal-toc ol { column-count: 1; }
    .legal-body { padding: 0 1rem 4rem; }
    .legal-topbar { padding: 0.875rem 1rem; }
}

/* =====================================================================
   Analytics Overview: /analytics/overview
   Pulse strip. Each card body is a single column (the narrative column lives in the header
   tooltips), so the cards can sit in a grid unchanged.
   ===================================================================== */

/* Activity pulse strip — calm neutral strip (paper + hairline border, like the
   rest of the app's surfaces), only rendered when events_30d > 0. No navy block. */
.analytics-pulse-strip {
    background: var(--brand-paper);
    color: var(--brand-body);
    border: 1px solid var(--brand-line);
    border-radius: var(--border-radius);
    padding: .65rem 1.25rem;
}
.analytics-pulse-strip .kpi-figure {
    font-size: 1.15rem;
    font-family: var(--brand-font-sans);
    font-weight: 700;
    color: var(--brand-navy);
}

/* =====================================================================
   ENTITY-DETAIL HERO — shared identity header for detail pages
   (country/detail, category/detail, location/detail). Tokens only.
   ===================================================================== */
.entity-card { overflow: hidden; }
.entity-hero {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1.5rem 1.5rem 1.25rem;
    background: linear-gradient(135deg, var(--brand-navy) 0%, var(--brand-indigo) 100%);
    color: var(--brand-white);
}
.entity-chip {
    width: 56px;
    height: 56px;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 14px;
    background: rgba(255, 255, 255, .12);
    color: var(--brand-white);
    font-size: 1.6rem;
}
.entity-hero-text { min-width: 0; }
.entity-hero .eyebrow { color: var(--brand-blue-bright); margin-bottom: .15rem; }
.entity-name {
    font-family: var(--brand-font-sans);
    font-weight: 600;
    font-size: 1.5rem;
    color: var(--brand-white);
    margin: 0 0 .35rem;
    line-height: 1.15;
    overflow-wrap: anywhere;
}
.entity-hero .status-pill { background: rgba(255, 255, 255, .16); color: var(--brand-white); border-color: rgba(255, 255, 255, .22); }
.entity-hero .status-pill.mono { font-family: var(--brand-font-mono); }

/* =====================================================================
   LOCATION SPATIAL MAPS — globe, floor-plan viewer, editor, dropzone.
   Indoor plan needs no tile server (air-gap safe). Brand tokens only.
   ===================================================================== */

/* --- mapa selector de coordenadas (Leaflet raster + Mapbox) del formulario de alta/edición de ubicación y del detalle.
   La altura vivía en el legacy override.css (que ya NO se carga), así que el contenedor colapsaba a ~2px y el mapa no se
   veía. Se restituye aquí (la hoja activa). --- */
.form-map { position: relative; height: 320px; width: 100%; border-radius: 10px; overflow: hidden; }
.detail-map { position: relative; height: 300px; width: 100%; border-radius: 10px; overflow: hidden; }
/* Sin conexión no hay mapa que mostrar: el contenedor se colapsa a la fila compacta del aviso. */
.detail-map.detail-map--empty { height: auto; }

/* --- globe (Mapbox) on the locations index + GEO root detail --- */
.lmap-globe-card { overflow: hidden; }
.lmap-globe { width: 100%; height: 420px; }
.lmap-globe .mapboxgl-popup-content { border-radius: 10px; padding: .5rem .7rem; box-shadow: 0 12px 30px rgba(14, 42, 69, .16); }
/* "Estás aquí": pin verde de marca + etiqueta con el nombre, para distinguir la ubicación que se está viendo */
.lmap-globe-highlight {
    color: var(--brand-green);
    font-size: 2.3rem; line-height: 1; cursor: default;
    filter: drop-shadow(0 3px 6px rgba(14, 42, 69, .35));
}
.lmap-globe-hl-popup .mapboxgl-popup-content {
    background: var(--brand-green); border-radius: 8px; padding: .25rem .6rem;
    box-shadow: 0 8px 20px rgba(23, 204, 152, .35);
}
.lmap-globe-hl-popup.mapboxgl-popup-anchor-bottom .mapboxgl-popup-tip { border-top-color: var(--brand-green); }
.lmap-globe-hl-popup.mapboxgl-popup-anchor-top .mapboxgl-popup-tip { border-bottom-color: var(--brand-green); }
.lmap-globe-hl-name { color: #fff; font-weight: 700; font-size: .8rem; white-space: nowrap; }

/* --- the spatial panel shell (detail page) --- */
.lmap-panel-card { overflow: hidden; }
.lmap-panel,
.lmap-panel-card .lmap-panel { position: relative; width: 100%; height: 480px; }
.lmap-panel .lmap-globe { height: 480px; }

/* Sin plano: el panel NO ocupa la altura de un mapa vacío; se colapsa a una fila compacta (subir/aviso). */
.lmap-panel.lmap-panel--empty,
.lmap-panel-card .lmap-panel.lmap-panel--empty { height: auto; background: transparent; }
.lmap-emptyrow {
    display: flex; align-items: center; gap: .6rem;
    padding: .7rem .9rem; color: var(--brand-body); font-size: .85rem;
}
.lmap-emptyrow-icon { font-size: 1.2rem; color: var(--brand-muted); }
.lmap-emptyrow-text { font-weight: 600; color: var(--brand-navy); }
.lmap-emptyrow-hint { color: var(--brand-muted); font-size: .78rem; }
/* variante "subir": la fila entera es zona de arrastre/clic (discreta, no obliga a cargar un plano) */
.lmap-emptyrow.is-upload {
    margin: .5rem; cursor: pointer;
    border: 1.5px dashed var(--brand-line); border-radius: 10px;
    transition: border-color .15s ease, background .15s ease;
}
.lmap-emptyrow.is-upload .lmap-emptyrow-icon { color: var(--brand-blue-deep); }
.lmap-emptyrow.is-upload:hover,
.lmap-emptyrow.is-upload.is-over { border-color: var(--brand-blue-deep); background: var(--brand-navy-tint); }

/* --- the floor-plan canvas (Leaflet CRS.Simple) --- */
.lmap-panel { background: var(--brand-canvas); }
.lmap-canvas { position: absolute; inset: 0; width: 100%; height: 100%; background: var(--brand-canvas); }
.lmap-canvas.leaflet-container { font-family: var(--brand-font-sans); }
.lmap-overlay { image-rendering: auto; filter: saturate(.92); }
/* plan fades in once the overlay image loads */
.lmap-overlay { animation: lmapFade .5s ease; }
@keyframes lmapFade { from { opacity: 0; } to { opacity: 1; } }

/* zone polygons + dot markers (canvas-rendered; labels are DOM tooltips) */
.lmap-zone-label.leaflet-tooltip,
.lmap-dot-label.leaflet-tooltip {
    background: rgba(255, 255, 255, .92);
    border: 1px solid var(--brand-line);
    border-radius: 7px;
    box-shadow: 0 4px 12px rgba(14, 42, 69, .10);
    color: var(--brand-navy);
    font-family: var(--brand-font-sans);
    font-size: .72rem;
    font-weight: 600;
    padding: .15rem .45rem;
}
.lmap-zone-label.leaflet-tooltip::before,
.lmap-dot-label.leaflet-tooltip::before { display: none; }
.lmap-zone-name { color: var(--brand-navy); }
.lmap-zone-badge {
    display: inline-block; margin-left: .35rem; padding: 0 .35rem;
    border-radius: 999px; background: var(--brand-navy-tint);
    color: var(--brand-blue-deep); font-size: .64rem; font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* branded dot pin (divIcon). El color base puede teñirse por ocupación (heat) vía --dot-heat (lo fija map.js por
   nodo); hover/activo declaran color explícito y ganan por especificidad, así el resalte sigue funcionando. */
.lmap-dot-pin {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px;
    color: var(--dot-heat, var(--brand-navy));
    font-size: 1.05rem;
    filter: drop-shadow(0 2px 3px rgba(14, 42, 69, .25));
    transition: transform .15s ease, color .15s ease;
}
.lmap-dot:hover .lmap-dot-pin { transform: scale(1.18); color: var(--brand-blue-deep); }
.lmap-dot.is-active .lmap-dot-pin { color: var(--brand-blue-deep); transform: scale(1.18); }

/* --- breadcrumb rail over the plan --- */
.lmap-crumb {
    position: absolute; bottom: 12px; left: 12px; z-index: 600;
    display: flex; align-items: center; flex-wrap: wrap; gap: .15rem;
    max-width: calc(100% - 24px);
    background: rgba(255, 255, 255, .94);
    border: 1px solid var(--brand-line);
    border-radius: 10px;
    padding: .3rem .5rem;
    box-shadow: 0 6px 18px rgba(14, 42, 69, .10);
}
.lmap-crumb-item {
    border: 0; background: transparent;
    color: var(--brand-blue-deep); font-weight: 600;
    font-size: .76rem; padding: .1rem .35rem; border-radius: 6px;
    cursor: pointer; transition: background .15s ease, color .15s ease;
}
.lmap-crumb-item:hover { background: var(--brand-navy-tint); color: var(--brand-navy); }
.lmap-crumb-item.is-current { color: var(--brand-navy); cursor: default; }
.lmap-crumb-sep { color: var(--brand-muted); font-size: .7rem; }

/* --- floating dot info card --- */
.lmap-info {
    position: absolute; right: 12px; bottom: 12px; z-index: 650;
    width: 220px;
    background: var(--brand-white);
    border: 1px solid var(--brand-line);
    border-radius: 12px;
    padding: .85rem .9rem;
    box-shadow: 0 14px 36px rgba(14, 42, 69, .16);
    animation: lmapFade .25s ease;
}
.lmap-info-close {
    position: absolute; top: .4rem; right: .4rem;
    border: 0; background: transparent; color: var(--brand-muted);
    cursor: pointer; line-height: 1; padding: .15rem;
}
.lmap-info-close:hover { color: var(--brand-navy); }
.lmap-info-name { font-weight: 600; color: var(--brand-navy); margin-bottom: .25rem; }
.lmap-info-tags { font-size: .76rem; color: var(--brand-body); margin-bottom: .6rem; }
.lmap-info-tags i { color: var(--brand-blue-deep); }
.lmap-info-link {
    display: inline-flex; align-items: center; gap: .35rem;
    text-decoration: none; color: var(--brand-blue-deep); font-weight: 600; font-size: .8rem;
}
.lmap-info-link:hover { color: var(--brand-navy); }

/* --- editor: toolbar buttons over the plan --- */
/* banda de ayuda del editor (abajo-derecha): explica las herramientas sin chocar con los botones (arriba), el
   breadcrumb (abajo-izquierda) ni el rail (arriba-derecha). La tarjeta de info no aparece en modo edición. */
.lmap-edit-help {
    position: absolute; bottom: 12px; right: 12px; z-index: 590;
    display: inline-flex; align-items: flex-start; gap: .4rem; max-width: min(60%, 420px);
    padding: .45rem .75rem; border-radius: 14px;
    background: rgba(255, 255, 255, .96); border: 1px solid var(--brand-line);
    box-shadow: 0 6px 18px rgba(14, 42, 69, .12);
    font-size: .75rem; line-height: 1.35; color: var(--brand-body);
}
.lmap-edit-help i { color: var(--brand-blue-deep); font-size: .95rem; margin-top: .05rem; flex: 0 0 auto; }
/* el texto puede ENVOLVER (antes se truncaba con puntos suspensivos y se perdía la explicación) */
.lmap-edit-help span { white-space: normal; }

.lmap-edit-toggle { position: absolute; top: 12px; right: 12px; z-index: 600; }
/* Reemplazar plano es DESTRUCTIVO (restablece/reescala posiciones): se viste como acción de peligro suave para que no
   se confunda con el toggle benigno de "Editar plano". Solo se tiñe de rojo de marca al pasar el cursor. */
.lmap-replace-plan {
    position: absolute; top: 12px; right: 130px; z-index: 600;
    border-color: var(--brand-danger-line, var(--brand-line));
    color: var(--brand-danger);
}
.lmap-replace-plan:hover { background: var(--brand-danger-tint); border-color: var(--brand-danger); color: var(--brand-danger); }
.lmap-file-hidden { display: none; }

/* --- chip "editando dentro de «zona»" (modo edición): contexto de anidación siempre visible --- */
.lmap-editing-chip {
    position: absolute; top: 12px; left: 50px; z-index: 600;
    display: inline-flex; align-items: center; gap: .4rem; max-width: 40%;
    padding: .3rem .7rem; border-radius: 999px;
    background: var(--brand-navy); color: #fff;
    font-size: .74rem; font-weight: 600;
    box-shadow: 0 6px 18px rgba(14, 42, 69, .22);
}
.lmap-editing-chip i { font-size: .9rem; opacity: .85; }
.lmap-editing-chip b { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* --- chip de estado de guardado (debounce de persistShape): Guardando… / Guardado / error de marca --- */
.lmap-save-status {
    position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%); z-index: 610;
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .3rem .75rem; border-radius: 999px;
    background: rgba(255, 255, 255, .97); border: 1px solid var(--brand-line);
    box-shadow: 0 6px 18px rgba(14, 42, 69, .14);
    font-size: .74rem; font-weight: 600; color: var(--brand-body);
    opacity: 0; pointer-events: none; transition: opacity .2s ease;
}
.lmap-save-status.is-shown { opacity: 1; }
.lmap-save-status.is-saving { color: var(--brand-blue-deep); }
.lmap-save-status.is-saved i { color: var(--brand-positive); }
.lmap-save-status.is-error { color: var(--brand-danger); border-color: var(--brand-danger); }
.lmap-save-status .lmap-spin { animation: lmapSpin .7s linear infinite; display: inline-flex; }
@keyframes lmapSpin { to { transform: rotate(360deg); } }

/* --- capa de mapa de calor (L.imageOverlay del plano): Leaflet la posiciona/escala; sobre el plano, bajo zonas/pines --- */
.lmap-heat { pointer-events: none; }

/* --- toggle encender/apagar el mapa de calor (arriba-izquierda, bajo el zoom, sobre la leyenda) --- */
.lmap-heat-toggle {
    position: absolute; top: 84px; left: 10px; z-index: 601;
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .3rem .6rem; border-radius: 999px; cursor: pointer;
    background: rgba(255, 255, 255, .94); border: 1px solid var(--brand-line);
    box-shadow: 0 6px 18px rgba(14, 42, 69, .10);
    font-size: .72rem; font-weight: 600; color: var(--brand-muted);
    transition: color .15s ease, border-color .15s ease;
}
.lmap-heat-toggle i { font-size: .9rem; }
.lmap-heat-toggle.is-on { color: var(--brand-blue-deep); border-color: var(--brand-blue-deep); }
.lmap-heat-toggle.is-on i { color: #C93A2E; } /* la llama encendida en rojo del extremo caliente */
.lmap-heat-toggle:hover { color: var(--brand-navy); }

/* --- leyenda de ocupación (heat): escala fría->caliente. Va ARRIBA-IZQUIERDA, bajo el toggle, para no chocar con el
   breadcrumb (abajo-izq). Se oculta en edición (ese rincón lo usa la barra de Geoman) y cuando el calor está apagado. */
.lmap-legend {
    position: absolute; top: 122px; left: 10px; z-index: 600;
    display: inline-flex; align-items: center; gap: .45rem;
    padding: .3rem .6rem; border-radius: 10px;
    background: rgba(255, 255, 255, .94); border: 1px solid var(--brand-line);
    box-shadow: 0 6px 18px rgba(14, 42, 69, .10);
    font-size: .68rem; font-weight: 600; color: var(--brand-muted);
}
.lmap-legend-bar {
    width: 96px; height: 8px; border-radius: 999px;
    /* coincide con la paleta del campo de calor (HEAT_STOPS en map.js): frío azul -> caliente rojo */
    background: linear-gradient(90deg,
        rgb(40, 92, 170), rgb(56, 162, 168), rgb(92, 178, 96),
        rgb(240, 204, 72), rgb(232, 142, 52), rgb(201, 58, 46));
}
.lmap-legend-lo, .lmap-legend-hi { font-variant-numeric: tabular-nums; }

/* --- aviso "recarga para actualizar el plano" tras crear/ubicar una ubicación en el plano (arriba-centro) --- */
.lmap-reload-notice {
    position: absolute; top: 12px; left: 50%; transform: translateX(-50%); z-index: 660;
    display: inline-flex; align-items: center; gap: .5rem;
    padding: .35rem .5rem .35rem .8rem; border-radius: 999px;
    background: var(--brand-navy); color: #fff;
    box-shadow: 0 10px 28px rgba(14, 42, 69, .28);
    font-size: .76rem; font-weight: 600;
    animation: lmapFade .25s ease;
}
.lmap-reload-notice > i { font-size: .95rem; opacity: .9; }
.lmap-reload-btn {
    border: 0; border-radius: 999px; cursor: pointer;
    padding: .2rem .6rem; font-size: .74rem; font-weight: 700;
    background: #fff; color: var(--brand-navy);
}
.lmap-reload-btn:hover { background: var(--brand-navy-tint); }
.lmap-reload-close {
    border: 0; background: transparent; color: rgba(255, 255, 255, .8);
    cursor: pointer; padding: .1rem; line-height: 1; display: inline-flex;
}
.lmap-reload-close:hover { color: #fff; }

/* --- "sin ubicar" rail (editor) --- */
.lmap-rail {
    position: absolute; top: 56px; right: 12px; z-index: 600;
    width: 210px; max-height: calc(100% - 80px); overflow: auto;
    background: var(--brand-white);
    border: 1px solid var(--brand-line);
    border-radius: 12px;
    box-shadow: 0 10px 28px rgba(14, 42, 69, .12);
}
.lmap-rail-head {
    display: flex; align-items: center; gap: .35rem;
    padding: .6rem .75rem; border-bottom: 1px solid var(--brand-line-soft);
    font-weight: 600; font-size: .78rem; color: var(--brand-navy);
}
.lmap-rail-count {
    margin-left: auto; padding: 0 .4rem; border-radius: 999px;
    background: var(--brand-navy-tint); color: var(--brand-blue-deep);
    font-size: .68rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.lmap-rail-body { padding: .35rem; }
.lmap-rail-item {
    display: flex; align-items: center; gap: .45rem;
    padding: .4rem .5rem; border-radius: 8px;
    font-size: .78rem; color: var(--brand-body); cursor: default;
}
.lmap-rail-item i { color: var(--brand-blue-deep); }
.lmap-rail-item:hover { background: var(--brand-paper); }
.lmap-rail-item span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lmap-rail-item.is-placed i { color: var(--brand-navy); }
/* botón "quitar del plano" (DELETE /shape): discreto, se tiñe de rojo solo en su hover */
.lmap-rail-remove {
    flex-shrink: 0; border: 0; background: transparent; cursor: pointer;
    padding: .15rem; border-radius: 6px; color: var(--brand-muted);
    display: inline-flex; align-items: center; transition: background .15s ease, color .15s ease;
}
.lmap-rail-remove:hover { background: var(--brand-danger-tint); color: var(--brand-danger); }
.lmap-rail-empty { padding: .6rem .75rem; color: var(--brand-muted); font-size: .76rem; }

/* --- panorama del edificio en la cabecera del rail: "N de M ubicadas" + barra de progreso --- */
.lmap-rail-overview {
    padding: .6rem .75rem .7rem;
    border-bottom: 1px solid var(--brand-line-soft);
    background: var(--brand-paper);
}
.lmap-rail-overview-top {
    display: flex; align-items: baseline; gap: .35rem; margin-bottom: .4rem;
    font-size: .74rem; color: var(--brand-body);
}
.lmap-rail-overview-num { font-weight: 700; color: var(--brand-navy); font-variant-numeric: tabular-nums; }
.lmap-rail-overview-bar { height: 6px; border-radius: 999px; background: var(--brand-line-soft); overflow: hidden; }
.lmap-rail-overview-fill {
    height: 100%; border-radius: 999px; background: var(--brand-positive);
    transition: width .5s cubic-bezier(.4, 0, .2, 1);
}

/* subtítulo de sección dentro del rail (zona actual / otras zonas / ubicadas aquí) */
.lmap-rail-sub {
    padding: .5rem .75rem .25rem;
    font-size: .64rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: var(--brand-muted);
}

/* item "sin ubicar" en la zona actual: clicar lo ARMA para que el próximo dibujo lo coloque */
.lmap-rail-item.is-unplaced { cursor: pointer; }
.lmap-rail-item.is-unplaced:hover { background: var(--brand-navy-tint); }
.lmap-rail-item.is-armed {
    background: var(--brand-navy-tint);
    box-shadow: inset 0 0 0 1.5px var(--brand-blue-deep);
}
.lmap-rail-item.is-armed > i { color: var(--brand-blue-deep); }
/* item "sin ubicar" en OTRA zona del edificio: clicar baja a su zona y lo arma */
.lmap-rail-item.is-elsewhere { cursor: pointer; color: var(--brand-muted); }
.lmap-rail-item.is-elsewhere:hover { background: var(--brand-paper); color: var(--brand-body); }
.lmap-rail-item.is-elsewhere .lmap-rail-where {
    margin-left: auto; font-size: .64rem; color: var(--brand-muted); flex: 0 0 auto;
}
/* botón "eliminar nodo" del rail (DELETE /tree, borra la ubicación) — distinto de la X de "quitar del plano" */
.lmap-rail-del {
    flex-shrink: 0; border: 0; background: transparent; cursor: pointer;
    padding: .15rem; border-radius: 6px; color: var(--brand-muted);
    display: inline-flex; align-items: center; transition: background .15s ease, color .15s ease;
}
.lmap-rail-del:hover { background: var(--brand-danger-tint); color: var(--brand-danger); }

/* indicador "armado": pista de qué se está colocando, en la banda de ayuda */
.lmap-edit-help.is-arming { border-color: var(--brand-blue-deep); }
.lmap-edit-help.is-arming i { color: var(--brand-blue-deep); }

/* --- empty / no-plan state + upload dropzone --- */
.lmap-empty {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .5rem; text-align: center; padding: 1.5rem;
}
.lmap-empty-icon { font-size: 2.4rem; color: var(--brand-muted); }
.lmap-empty-text { color: var(--brand-body); margin: 0; max-width: 360px; }
.lmap-dropzone {
    display: flex; flex-direction: column; align-items: center; gap: .25rem;
    margin-top: .5rem; padding: 1.4rem 2rem;
    border: 2px dashed var(--brand-line);
    border-radius: 14px;
    background: var(--brand-paper);
    color: var(--brand-blue-deep);
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease, transform .15s ease;
}
.lmap-dropzone i { font-size: 1.8rem; }
.lmap-dropzone:hover,
.lmap-dropzone.is-over { border-color: var(--brand-blue-deep); background: var(--brand-navy-tint); }
.lmap-dropzone.is-over { transform: scale(1.01); }
.lmap-dropzone-title { font-weight: 600; color: var(--brand-navy); }
.lmap-dropzone-hint { font-size: .76rem; color: var(--brand-muted); }
.lmap-file { display: none; }

/* --- loading skeleton --- */
.lmap-skeleton {
    position: absolute; inset: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .75rem; background: var(--brand-canvas);
}
.lmap-skeleton-pulse {
    width: 60%; max-width: 420px; height: 60%;
    border-radius: 14px;
    background: linear-gradient(100deg, var(--brand-line-soft) 30%, var(--brand-paper) 50%, var(--brand-line-soft) 70%);
    background-size: 200% 100%;
    animation: lmapSkeleton 1.3s ease-in-out infinite;
}
.lmap-skeleton-text { color: var(--brand-muted); font-size: .82rem; }
@keyframes lmapSkeleton { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* Geoman toolbar adopts brand tokens (override the plugin defaults) */
.leaflet-pm-toolbar .leaflet-pm-icon { filter: none; }
.leaflet-pm-toolbar .button-container a.leaflet-buttons-control-button {
    border-color: var(--brand-line);
}
.leaflet-pm-toolbar .active .leaflet-pm-actions-container,
.leaflet-pm-toolbar .button-container.active a { background-color: var(--brand-navy-tint); }

@media (max-width: 767.98px) {
    .lmap-globe, .lmap-panel, .lmap-panel .lmap-globe { height: 320px; }
    .lmap-rail { width: 170px; }
    .lmap-info { width: 180px; }
}

/* ===================================================================
   Árbol de ubicaciones (.loc-*) — vista /locations (js/location/tree.js)
   Dos modos sobre los mismos datos: (1) la fila de la tabla que "crece"
   mostrando ascendentes + hijos, y (2) el árbol jerárquico. Paleta Accendo
   (navy + blue-deep); el verde no se usa aquí. Tokens, sin hexes sueltos.
   =================================================================== */

/* --- píldora de conteo de sububicaciones (columna de la tabla) --- */
.loc-subcount {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .1rem .5rem; border-radius: 999px;
    background: var(--brand-navy-tint); color: var(--brand-blue-deep);
    font-size: .74rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.loc-subcount i { font-size: .85rem; }

/* --- chevron que expande/colapsa la fila de la lista --- */
.loc-grow-toggle {
    border: 0; background: transparent; cursor: pointer; padding: 0;
    width: 24px; height: 24px; margin-right: .35rem; vertical-align: middle;
    border-radius: 6px; color: var(--brand-muted);
    display: inline-flex; align-items: center; justify-content: center;
    transition: background .15s ease, color .15s ease;
}
.loc-grow-toggle:hover { background: var(--brand-navy-tint); color: var(--brand-blue-deep); }

/* --- panel que aparece bajo la fila (breadcrumb de ascendentes + hijos) --- */
.loc-detail-row > td { padding: 0; background: var(--brand-paper); border-top: 0; }
.loc-detail { display: flex; flex-direction: column; gap: .9rem; padding: 1rem 1.25rem 1.15rem; }
.loc-detail-loading { color: var(--brand-muted); font-size: .85rem; }
/* En la pestaña "Estructura" del detalle el contenedor ya está dentro de un tab con padding: se anula el propio. */
.loc-detail.loc-structure { padding: 0; }
.loc-structure-path { position: static; background: transparent; padding: 0; margin-bottom: .25rem; }
.loc-structure-path .loc-crumb-self { font-size: .92rem; }

.loc-crumb { display: flex; align-items: center; flex-wrap: wrap; gap: .35rem; }
.loc-crumb-lbl {
    text-transform: uppercase; letter-spacing: .04em; font-size: .66rem; font-weight: 700;
    color: var(--brand-muted); margin-right: .25rem;
}
.loc-crumb-root { color: var(--brand-muted); font-style: italic; font-size: .8rem; }
.loc-crumb-item {
    border: 0; background: transparent; cursor: pointer; padding: .1rem .3rem; border-radius: 6px;
    color: var(--brand-blue-deep); font-weight: 600; font-size: .8rem;
    transition: background .15s ease;
}
.loc-crumb-item:hover { background: var(--brand-navy-tint); }
.loc-crumb-sep { color: var(--brand-line); font-size: .8rem; }
.loc-crumb-self { color: var(--brand-navy); font-weight: 700; font-size: .8rem; }

.loc-section { display: flex; flex-direction: column; gap: .55rem; }
.loc-section-head { display: flex; align-items: center; gap: .6rem; }
.loc-section-lbl {
    text-transform: uppercase; letter-spacing: .04em; font-size: .66rem; font-weight: 700; color: var(--brand-muted);
}
.loc-add {
    display: inline-flex; align-items: center; gap: .25rem;
    border: 1px solid var(--brand-line); background: var(--brand-white);
    color: var(--brand-blue-deep); font-weight: 600; font-size: .73rem;
    padding: .2rem .55rem; border-radius: 999px; cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}
.loc-add:hover { border-color: var(--brand-blue-deep); background: var(--brand-navy-tint); }
.loc-add i { font-size: .85rem; }

.loc-chips { display: flex; flex-wrap: wrap; gap: .45rem; }
.loc-chip {
    display: inline-flex; align-items: center; gap: .35rem;
    border: 1px solid var(--brand-line); background: var(--brand-white);
    border-radius: 999px; padding: .28rem .65rem; cursor: pointer;
    font-size: .78rem; font-weight: 500; color: var(--brand-navy);
    transition: border-color .15s ease, background .15s ease;
}
.loc-chip:hover { border-color: var(--brand-blue-deep); background: var(--brand-navy-tint); }
.loc-chip i { color: var(--brand-blue-deep); font-size: .9rem; }
.loc-chip .n {
    margin-left: .1rem; padding: 0 .35rem; border-radius: 999px;
    background: var(--brand-navy-tint); color: var(--brand-blue-deep);
    font-size: .64rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.loc-empty { color: var(--brand-muted); font-size: .85rem; }

/* --- modo árbol jerárquico (#locTreeBody) --- */
.loc-tree { display: flex; flex-direction: column; }
.loc-tree-node { display: flex; flex-direction: column; }
.loc-tree-row {
    display: flex; align-items: center; gap: .4rem; min-height: 36px;
    padding: .3rem .5rem; border-radius: 8px; transition: background .12s ease;
}
.loc-tree-row:hover { background: var(--brand-paper); }
.loc-tree-toggle {
    flex-shrink: 0; width: 20px; height: 20px; padding: 0;
    border: 0; background: transparent; cursor: pointer; color: var(--brand-muted);
    display: inline-flex; align-items: center; justify-content: center;
}
.loc-tree-toggle:hover { color: var(--brand-blue-deep); }
.loc-tree-icon { flex-shrink: 0; color: var(--brand-blue-deep); font-size: 1rem; }
.loc-tree-name { color: var(--brand-navy); text-decoration: none; font-weight: 600; font-size: .85rem; }
.loc-tree-name:hover { color: var(--brand-blue-deep); text-decoration: underline; }
.loc-tree-count {
    padding: 0 .4rem; border-radius: 999px; background: var(--brand-navy-tint);
    color: var(--brand-blue-deep); font-size: .64rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.loc-tree-actions { margin-left: auto; display: inline-flex; gap: .15rem; opacity: 0; transition: opacity .12s ease; }
.loc-tree-row:hover .loc-tree-actions { opacity: 1; }
.loc-tree-act {
    border: 0; background: transparent; cursor: pointer; padding: .25rem; border-radius: 6px;
    color: var(--brand-muted); display: inline-flex; align-items: center;
    transition: background .15s ease, color .15s ease;
}
.loc-tree-act:hover { background: var(--brand-navy-tint); color: var(--brand-blue-deep); }
.loc-tree-act.is-danger:hover { background: var(--brand-danger-tint); color: var(--brand-danger); }
.loc-tree-children { display: flex; flex-direction: column; }
.loc-tree-loading, .loc-tree-empty { padding: .6rem .75rem; color: var(--brand-muted); font-size: .82rem; }

/* edición de estructura: arrastre de nodos (activado por "Editar estructura") */
body.loc-edit-on .loc-tree-row { cursor: grab; }
.loc-tree-row.is-dragging { opacity: .5; }
.loc-tree-row.is-dragover {
    background: var(--brand-navy-tint);
    outline: 2px dashed var(--brand-blue-deep); outline-offset: -2px;
}

/* Utilidad global para mostrar/ocultar por clase (el JS alterna .is-hidden, nunca estilos en línea). */
.is-hidden { display: none !important; }

/* ===================================================================
   Asistente de creación de inventarios (.ab-*)
   Paleta Accendo: navy + blue-deep. El verde esmeralda se reserva
   solo para éxito real, por lo que aquí NO se usa.
   =================================================================== */

.ab-stepper { display: flex; align-items: center; gap: 8px; margin-bottom: 1.75rem; }
.ab-step { display: flex; align-items: center; gap: 8px; }
.ab-step-dot {
    width: 28px; height: 28px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 0.82rem; background: var(--brand-paper); color: var(--brand-muted);
    border: 1px solid var(--brand-line);
}
.ab-step-label { font-size: 0.85rem; color: var(--brand-muted); }
.ab-step.is-active .ab-step-dot { background: var(--brand-navy); color: #fff; border-color: var(--brand-navy); }
.ab-step.is-active .ab-step-label { color: var(--brand-ink); font-weight: 600; }
.ab-step.is-done .ab-step-dot { background: var(--brand-navy-tint); color: var(--brand-navy); border-color: var(--brand-navy-tint-border); }
.ab-step.is-done .ab-step-label { color: var(--brand-blue-deep); }
.ab-step-line { flex: 1; height: 2px; background: var(--brand-line); border-radius: 2px; }

.ab-grid { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 16px; align-items: start; }
.ab-grid.ab-grid-single { grid-template-columns: 1fr; }
.ab-grid.ab-grid-single .ab-main { max-width: 680px; margin: 0 auto; width: 100%; }
.ab-main { min-width: 0; }
.ab-panel-title { font-size: 1.05rem; font-weight: 600; color: var(--brand-ink); margin-bottom: 0.25rem; }
.ab-panel-sub { font-size: 0.85rem; color: var(--brand-muted); margin-bottom: 1rem; }
.ab-hint { font-size: 0.8rem; color: var(--brand-muted); margin: 0.25rem 0 0.5rem; }
.ab-inline-error { font-size: 0.82rem; color: var(--bs-danger, #a3402d); margin-top: 0.75rem; }

.ab-loc-search { position: relative; margin-bottom: 12px; }
.ab-loc-search i { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); color: var(--brand-muted); font-size: 16px; }
.ab-loc-search .form-control { padding-left: 34px; }

.ab-tree-wrap { position: relative; }
.ab-tree-busy {
    display: flex; align-items: center; gap: 8px; font-size: 0.82rem; color: var(--brand-navy);
    padding: 8px 10px; margin-bottom: 8px; background: var(--brand-navy-tint);
    border: 1px solid var(--brand-navy-tint-border); border-radius: 6px;
}
.ab-tree.is-processing { pointer-events: none; opacity: 0.55; }
.ab-tree { border: 1px solid var(--brand-line); border-radius: 8px; max-height: 380px; overflow-y: auto; padding: 6px; }
.ab-tree-loading { color: var(--brand-muted); font-size: 0.85rem; padding: 12px; }
.ab-tree-row { display: flex; align-items: center; gap: 10px; padding: 6px 8px; border-radius: 6px; cursor: pointer; }
.ab-tree-row:hover { background: var(--brand-paper); }
.ab-tree-row.is-busy { pointer-events: none; opacity: 0.7; }
.ab-check {
    width: 20px; height: 20px; min-width: 20px; border-radius: 4px;
    border: 1px solid var(--brand-line); display: inline-flex; align-items: center; justify-content: center;
    font-size: 13px; background: #fff;
}
.ab-check.is-checked { background: var(--brand-navy); border-color: var(--brand-navy); color: #fff; }
.ab-check.is-indeterminate { background: var(--brand-navy-tint); border-color: var(--brand-navy-tint-border); color: var(--brand-navy); }
.ab-check.is-loading { border-color: var(--brand-line); background: #fff; color: var(--brand-blue-deep); }
.ab-check.is-loading .spinner-border { width: 12px; height: 12px; border-width: 0.15em; }
.ab-caret { font-size: 15px; color: var(--brand-muted); width: 16px; text-align: center; }
.ab-caret-spacer { width: 16px; display: inline-block; }
.ab-role-icon { font-size: 17px; color: var(--brand-blue-deep); }
.ab-node-name { color: var(--brand-ink); }
.ab-node-badge {
    margin-left: auto; font-size: 0.72rem; color: var(--brand-navy);
    background: var(--brand-navy-tint); border: 1px solid var(--brand-navy-tint-border);
    padding: 1px 8px; border-radius: 6px;
}

.ab-skip-report {
    display: flex; gap: 8px; align-items: flex-start; margin-top: 12px; padding: 8px 10px;
    border-radius: 6px; font-size: 0.8rem; background: var(--brand-navy-tint); color: var(--brand-navy);
}
.ab-skip-report i { margin-top: 1px; }
.ab-skip-report.is-warn { background: rgba(181, 133, 42, 0.12); color: var(--brand-warning); }
.ab-skip-report ul { padding-left: 18px; }

.ab-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.ab-state-chip {
    border: 1px solid var(--brand-line); background: #fff; color: var(--brand-ink);
    border-radius: 999px; padding: 5px 14px; font-size: 0.85rem; cursor: pointer;
}
.ab-state-chip.is-on { background: var(--brand-navy); color: #fff; border-color: var(--brand-navy); }
.ab-cat-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.ab-chip {
    display: inline-flex; align-items: center; gap: 6px; font-size: 0.8rem;
    background: var(--brand-navy-tint); color: var(--brand-navy); padding: 3px 10px; border-radius: 6px;
}
.ab-chip.is-removable i { cursor: pointer; font-size: 14px; }

.ab-field-help { font-size: 0.78rem; color: var(--brand-muted); margin-top: 5px; }

.ab-review { margin: 0; }
.ab-review > div { display: flex; justify-content: space-between; gap: 16px; padding: 8px 0; }
.ab-review > div:last-child { border-top: 1px solid var(--brand-line); margin-top: 6px; padding-top: 12px; }
.ab-review dt { color: var(--brand-muted); font-weight: 400; font-size: 0.85rem; }
.ab-review dd { margin: 0; color: var(--brand-ink); text-align: right; }
.ab-rev-strong { color: var(--brand-navy); font-weight: 600; font-size: 1.05rem; }

.ab-rail { display: flex; flex-direction: column; gap: 12px; }
.ab-rail-card { background: #fff; border: 1px solid var(--brand-line); border-radius: 12px; padding: 1rem; }
.ab-rail-expected { display: flex; align-items: baseline; gap: 10px; padding-bottom: 14px; margin-bottom: 10px; border-bottom: 1px solid var(--brand-line-soft); }
.ab-rail-kpi-spinner { display: inline-flex; align-items: center; min-height: 1.9rem; }
.ab-rail-kpi-spinner .spinner-border { width: 1.35rem; height: 1.35rem; border-width: 0.18em; }
.ab-rail-kpi-figure { font-size: 1.9rem; font-weight: 600; color: var(--brand-navy); line-height: 1; }
.ab-rail-kpi-label { display: flex; flex-direction: column; font-size: 0.82rem; color: var(--brand-muted); }
.ab-rail-kpi-hint { font-size: 0.72rem; color: var(--brand-muted); }
.ab-rail-title { font-size: 0.9rem; font-weight: 600; color: var(--brand-ink); margin-bottom: 10px; }
.ab-rail-row { display: flex; align-items: center; gap: 8px; font-size: 0.85rem; padding: 5px 0; color: var(--brand-muted); }
.ab-rail-row i { font-size: 16px; color: var(--brand-muted); }
.ab-rail-val { margin-left: auto; font-weight: 500; color: var(--brand-ink); }
.ab-rail-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--brand-line-soft); }

.ab-footer { display: flex; justify-content: space-between; align-items: center; margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--brand-line-soft); }
.ab-footer-right { display: flex; gap: 8px; margin-left: auto; }
/* Phones: the footer stacks with the action group on top and Cancelar or Anterior at the bottom, and
   every button spans the row so a thumb reaches it. column-reverse keeps the DOM order (cancel
   first) for keyboard and screen readers. The same rule serves the builders and the 14 forms. */
@media (max-width: 575.98px) {
    .ab-footer { flex-direction: column-reverse; align-items: stretch; gap: .5rem; }
    .ab-footer-right { flex-direction: column-reverse; margin-left: 0; gap: .5rem; }
    .ab-footer .btn { width: 100%; justify-content: center; }
}

@media (max-width: 991.98px) {
    .ab-grid { grid-template-columns: 1fr; }
    .ab-step-label { display: none; }
}

/* =====================================================================
   BARRAS DE PROGRESO EN LÍNEA — primitiva compartida
   Estructura: contenedor flex + .track (fondo gris) + .fill (color de marca) + .pct/.cnt.
   Cada variante define solo lo que la distingue: altura, color de fill, animaciones, labels.
   Variantes: wo-progress, presence-bar, generation-meter, picking-progress, picking-monitor-bar.
   ===================================================================== */

/* — work-order list: 6px, navy fill — */
.wo-progress { display: flex; align-items: center; gap: 8px; min-width: 120px; }
.wo-progress .cnt { font-size: 0.78rem; color: var(--brand-muted); white-space: nowrap; }

/* track + fill compartidos: wo-progress, presence-bar y generation-meter son idénticos (6px, navy) */
.wo-progress .track,
.presence-page .presence-bar .track,
.tag-generation-page .generation-meter .track { flex: 1; height: 6px; border-radius: 999px; background: var(--brand-line); overflow: hidden; }
.wo-progress .fill,
.presence-page .presence-bar .fill,
.tag-generation-page .generation-meter .fill { display: block; height: 100%; border-radius: 999px; background: var(--brand-navy); }

/* ===== Resultado de auditoría (cross): descripción truncada ===== */
.audit-desc {
    display: inline-block; max-width: 280px; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; vertical-align: bottom; color: var(--brand-muted);
}


/* ===========================================================================
   PICKING — Allocation Engine UI (/operations/picking)
   List KPI strip, in-row progress bar, the live "engine rail" builder preview,
   and the creation chooser. Tokens only — no hardcoded hexes.
   =========================================================================== */

/* ---- KPI strip on the list (one figure per metric) ---- */
.picking-kpi .card-body { padding: .85rem 1rem .7rem; }
.picking-kpi .lbl { font-size: .72rem; color: var(--brand-muted); display: flex; align-items: center; gap: .4rem; margin-bottom: .5rem; }
.picking-kpi .lbl i { font-size: 1rem; color: var(--brand-blue-deep); }
.picking-kpi .fig { font-family: var(--brand-font-mono); font-size: 1.6rem; font-weight: 600; color: var(--brand-ink); line-height: 1; font-variant-numeric: tabular-nums; }
.picking-kpi .fig.is-live { color: var(--brand-navy); }

/* ---- in-row progress + monitor banner: estructura compartida (ver sección barras arriba) ---- */
.picking-progress,
.picking-monitor-bar { display: flex; align-items: center; }
.picking-progress .track,
.picking-monitor-bar .track { flex: 1; border-radius: 20px; background: var(--brand-line); overflow: hidden; }
.picking-progress .fill,
.picking-monitor-bar .fill { height: 100%; border-radius: 20px; background: var(--brand-positive); }

/* — list column: 7px, pct + cnt labels — */
.picking-progress { gap: .6rem; min-width: 150px; }
.picking-progress .track { height: 7px; }
.picking-progress .pct { font-family: var(--brand-font-mono); font-size: .78rem; color: var(--brand-ink); width: 2.6rem; text-align: right; font-variant-numeric: tabular-nums; }
.picking-progress .cnt { font-family: var(--brand-font-mono); font-size: .72rem; color: var(--brand-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* — monitor banner: 10px, animated, large pct — */
.picking-monitor-bar { gap: .9rem; }
.picking-monitor-bar .track { height: 10px; }
.picking-monitor-bar .fill { transition: width 1s cubic-bezier(.4, 0, .2, 1); }
.picking-monitor-bar .pct { font-family: var(--brand-font-mono); font-size: 1.1rem; font-weight: 600; color: var(--brand-navy); font-variant-numeric: tabular-nums; }

/* — export progress modal (partials/progress_modal.html): a 20px track; the bar starts empty and the scripts
   write its computed width (the one inline style left, set at run time) — */
.progress-modal-track { height: 20px; }
.progress-modal-track .progress-bar { width: 0; }

/* — loan detail page: signature frame, picture, placeholder glyph and pad, and the round empty-state disc — */
.lend-detail-page .signature-frame { min-height: 160px; display: flex; align-items: center; justify-content: center; }
.lend-detail-page .signature-image { max-height: 150px; object-fit: contain; }
.lend-detail-page .signature-placeholder { font-size: 3rem; }
.signature-pad { min-height: 200px; }
.lend-detail-page .empty-icon-disc { width: 60px; height: 60px; }

/* strategy chip (list + manifest) */
.picking-strat { font-family: var(--brand-font-mono); font-size: .75rem; color: var(--brand-body);
    background: var(--brand-paper); border: 1px solid var(--brand-line); border-radius: 5px; padding: .12rem .5rem; }

/* .picking-live-dot retirado: los estados en curso ahora usan .status-pill.is-live. El guard de
   reduced-motion se conserva para la pastilla viva. */
@media (prefers-reduced-motion: reduce) { .status-pill.is-live::before { animation: none; } }

/* ---- builder layout: form + sticky engine rail ---- */
.picking-builder-grid { display: grid; grid-template-columns: 1fr 400px; gap: 1.25rem; align-items: start; }
@media (max-width: 991.98px) { .picking-builder-grid { grid-template-columns: 1fr; } }

/* strategy <select> + explanation panel */
.picking-stratbox { display: flex; gap: .9rem; align-items: stretch; flex-wrap: wrap; }
.picking-stratbox .picking-stratsel { flex: 0 0 230px; max-width: 100%; }
.picking-stratexp { flex: 1; min-width: 200px; background: var(--brand-paper); border: 1px solid var(--brand-line);
    border-radius: var(--border-radius); padding: .7rem .9rem; display: flex; gap: .7rem; align-items: flex-start; }
.picking-stratexp i { font-size: 1.2rem; color: var(--brand-blue-deep); margin-top: 1px; flex-shrink: 0; }
.picking-stratexp .h { font-size: .82rem; font-weight: 600; color: var(--brand-ink); }
.picking-stratexp .d { font-size: .78rem; color: var(--brand-muted); margin-top: .15rem; line-height: 1.5; }

/* demand-line table — packing-unit reference cell */
.picking-line-ref { display: flex; align-items: center; gap: .55rem; }
.picking-line-ref .ic { width: 30px; height: 30px; border-radius: 7px; background: var(--brand-navy-tint);
    color: var(--brand-navy); display: flex; align-items: center; justify-content: center; font-size: 1rem; flex-shrink: 0; }
.picking-line-ref .code { font-family: var(--brand-font-mono); font-size: .82rem; color: var(--brand-ink); font-weight: 500; }
.picking-line-ref .sub { color: var(--brand-muted); font-size: .72rem; margin-top: 1px; }
.picking-qty-input { font-family: var(--brand-font-mono); width: 4rem; text-align: right; }

/* dashed "add line" button */
.picking-addline { font-size: .82rem; font-weight: 500; color: var(--brand-blue-deep); background: transparent;
    border: 1px dashed var(--brand-navy-tint-border); border-radius: var(--border-radius); padding: .6rem .9rem;
    width: 100%; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: .45rem; transition: .18s; }
.picking-addline:hover { background: var(--brand-paper); border-color: var(--brand-blue-deep); }

/* inline hint under the line table */
.picking-help { font-size: .78rem; color: var(--brand-muted); margin-top: .65rem; display: flex; gap: .45rem; align-items: flex-start; }
.picking-help i { color: var(--brand-blue-deep); font-size: .9rem; margin-top: 1px; flex-shrink: 0; }

/* ---- engine rail (the live preview) ---- */
.picking-rail { position: sticky; top: 1.5rem; }
.picking-rail-head { background: var(--brand-navy); border-radius: 10px 10px 0 0; padding: .95rem 1.35rem;
    display: flex; justify-content: space-between; align-items: center; }
.picking-rail-head h3 { color: var(--brand-white); font-size: .9rem; margin: 0; display: flex; align-items: center; gap: .55rem; }
.picking-rail-head .live { font-size: .68rem; font-weight: 500; color: var(--brand-blue-bright);
    font-family: var(--brand-font-mono); display: flex; align-items: center; gap: .4rem; letter-spacing: .06em; }
.picking-rail-head .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--brand-green); animation: picking-rail-pulse 2s infinite; }
@keyframes picking-rail-pulse { 0% { box-shadow: 0 0 0 0 rgba(23, 204, 152, .5); } 70% { box-shadow: 0 0 0 7px rgba(23, 204, 152, 0); } 100% { box-shadow: 0 0 0 0 rgba(23, 204, 152, 0); } }
@media (prefers-reduced-motion: reduce) { .picking-rail-head .dot { animation: none; } }
.picking-rail-body { background: var(--brand-white); border: 1px solid var(--brand-line); border-top: none;
    border-radius: 0 0 10px 10px; box-shadow: 0 1px 2px rgba(var(--brand-navy-rgb), .04); }

/* feasibility headline + bar */
.picking-feas { padding: 1.25rem 1.35rem 1rem; }
.picking-feas .big { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .2rem; }
.picking-feas .big .fig { font-family: var(--brand-font-mono); font-size: 2.3rem; font-weight: 500;
    color: var(--brand-ink); letter-spacing: -.02em; line-height: 1; font-variant-numeric: tabular-nums; }
.picking-feas .big .of { font-size: .95rem; color: var(--brand-muted); font-family: var(--brand-font-mono); }
.picking-feas .cap { font-size: .78rem; color: var(--brand-muted); margin-bottom: .9rem; }
.picking-bar { height: 9px; border-radius: 20px; background: var(--brand-line); overflow: hidden; display: flex; }
.picking-bar .fill { height: 100%; background: var(--brand-positive); width: 0; transition: width 1.1s cubic-bezier(.4, 0, .2, 1); }
.picking-bar .short { height: 100%; background: var(--brand-warning); opacity: .55; width: 0; transition: width 1.1s cubic-bezier(.4, 0, .2, 1) .1s; }
.picking-bar-labels { display: flex; justify-content: space-between; margin-top: .5rem; font-size: .72rem; color: var(--brand-muted); }

/* rail KPI trio */
.picking-rail-kpis { display: grid; grid-template-columns: repeat(3, 1fr);
    border-top: 1px solid var(--brand-line-soft); border-bottom: 1px solid var(--brand-line-soft); }
.picking-rail-kpis .k { padding: .8rem .9rem; border-right: 1px solid var(--brand-line-soft); }
.picking-rail-kpis .k:last-child { border-right: none; }
.picking-rail-kpis .k-lbl { font-size: .65rem; letter-spacing: .05em; text-transform: uppercase; color: var(--brand-muted); margin-bottom: .25rem; }
.picking-rail-kpis .k-val { font-family: var(--brand-font-mono); font-size: 1.25rem; font-weight: 500; color: var(--brand-ink); font-variant-numeric: tabular-nums; }
.picking-rail-kpis .k-val.is-warn { color: var(--brand-warning); }
.picking-rail-kpis .k-val.is-ok { color: var(--brand-positive-text); }

/* "what & why" list */
.picking-why { padding: 1rem 1.35rem .5rem; }
.picking-why .ttl { font-size: .78rem; font-weight: 600; color: var(--brand-ink); margin-bottom: .2rem; display: flex; align-items: center; gap: .45rem; }
.picking-why .ttl i { color: var(--brand-blue-deep); font-size: 1rem; }
.picking-why .sub { font-size: .72rem; color: var(--brand-muted); margin-bottom: .75rem; }
.picking-epc { display: flex; align-items: center; gap: .7rem; padding: .6rem 0; border-top: 1px solid var(--brand-line-soft); }
.picking-epc:first-of-type { border-top: none; }
.picking-epc .ord { width: 21px; height: 21px; border-radius: 50%; background: var(--brand-navy-tint); color: var(--brand-navy);
    font-family: var(--brand-font-mono); font-size: .66rem; font-weight: 500; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.picking-epc .info { flex: 1; min-width: 0; }
.picking-epc .serial { font-family: var(--brand-font-mono); font-size: .78rem; color: var(--brand-ink); font-weight: 500; }
.picking-epc .meta { font-size: .7rem; color: var(--brand-muted); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.picking-epc .reason { text-align: right; flex-shrink: 0; }
.picking-epc .why-pill { font-family: var(--brand-font-mono); font-size: .7rem; font-weight: 500;
    color: var(--brand-positive-text); background: var(--brand-positive-tint); padding: .18rem .55rem; border-radius: 20px; }
.picking-epc .r2 { font-size: .66rem; color: var(--brand-muted); margin-top: .2rem; }
.picking-morelink { font-size: .72rem; color: var(--brand-blue-deep); font-weight: 500; padding: .6rem 0 .25rem; display: inline-block; }

/* rail footer actions */
.picking-rail-foot { padding: .95rem 1.35rem; border-top: 1px solid var(--brand-line-soft); display: flex; gap: .6rem; }
.picking-rail-foot .btn { flex: 1; }

/* empty preview placeholder */
.picking-rail-empty { padding: 2rem 1.35rem; text-align: center; color: var(--brand-muted); font-size: .8rem; }
.picking-rail-empty i { font-size: 1.6rem; display: block; margin-bottom: .5rem; color: var(--brand-navy-tint-border); }

/* ---- creation chooser tiles (matches creation_modes look) ---- */
.picking-choice .tile { width: 54px; height: 54px; border-radius: 12px; display: flex; align-items: center;
    justify-content: center; font-size: 1.6rem; flex-shrink: 0; }
.picking-choice .tile.web { background: var(--brand-navy-tint); color: var(--brand-navy); }
.picking-choice .tile.xls { background: var(--brand-positive-tint); color: var(--brand-positive-text); }
.picking-choice .go { margin-top: .7rem; font-size: .78rem; font-weight: 500; color: var(--brand-blue-deep); display: inline-flex; align-items: center; gap: .3rem; }
/* Afordancia de hover de la tarjeta de modo de creacion activa (no aplica a la deshabilitada). */
.picking-choice { transition: box-shadow .15s ease, border-color .15s ease; }
.picking-choice:not(.is-disabled):hover { border-color: var(--brand-blue-deep); box-shadow: 0 .5rem 1.25rem rgba(14, 42, 69, .12); }
/* Modo de creacion no disponible aun (carga por Excel): tarjeta atenuada, no interactiva. */
.picking-choice.is-disabled { opacity: .6; cursor: not-allowed; }
.picking-choice.is-disabled .tile { filter: grayscale(.5); }
.picking-soon { background: var(--brand-line); color: var(--brand-muted); font-weight: 500;
    font-size: .62rem; letter-spacing: .03em; text-transform: uppercase; vertical-align: middle; }

/* .is-hidden ya definida arriba (utilidad global mostrar/ocultar): se elimina el duplicado. */

/* Picking builder — anchos de columna de la tabla de lineas (sin estilos en linea). */
#picking-lines-table { table-layout: fixed; }
#picking-lines-table .picking-col-pu   { width: 45%; }
#picking-lines-table .picking-col-qty  { width: 12%; }
#picking-lines-table .picking-col-dest { width: 33%; }
#picking-lines-table .picking-col-act  { width: 10%; }

/* ====== Detalle a escala: pestañas de estado, drill-down de seriales, paginador ====== */
/* Pestañas de filtro por estado de línea (con conteo). */
.picking-tabs,
.presence-tabs { display: flex; flex-wrap: wrap; gap: .4rem; }
.picking-tab,
.presence-tab { display: inline-flex; align-items: center; gap: .45rem; padding: .4rem .8rem; border-radius: 8px;
    border: 1px solid var(--brand-line); background: var(--brand-paper); color: var(--brand-body);
    font-size: .82rem; font-weight: 500; text-decoration: none; transition: background .12s, border-color .12s; }
.picking-tab:hover,
.presence-tab:hover { border-color: var(--brand-blue-deep); color: var(--brand-navy); }
.picking-tab.is-active,
.presence-tab.is-active { background: var(--brand-navy); border-color: var(--brand-navy); color: #fff; }
.picking-tab .n,
.presence-tab .n { font-family: var(--brand-font-mono); font-size: .72rem; padding: .04rem .4rem; border-radius: 20px;
    background: var(--brand-line); color: var(--brand-ink); font-variant-numeric: tabular-nums; }
.picking-tab.is-active .n,
.presence-tab.is-active .n { background: rgba(255, 255, 255, .22); color: #fff; }

/* Fila expandible con el drill-down de seriales de una línea. */
.picking-drill-row > td { background: var(--brand-navy-tint); padding: 0; }
.picking-drill { padding: .9rem 1.1rem; }
.picking-drill-tabs { display: inline-flex; gap: .25rem; margin-bottom: .7rem; background: var(--brand-paper);
    border: 1px solid var(--brand-line); border-radius: 8px; padding: .2rem; }
.picking-lens { border: 0; background: transparent; color: var(--brand-muted); font-size: .78rem; font-weight: 500;
    padding: .28rem .7rem; border-radius: 6px; cursor: pointer; }
.picking-lens:hover { color: var(--brand-navy); }
.picking-lens.is-active { background: var(--brand-blue-deep); color: #fff; }
.picking-drill-count { font-family: var(--brand-font-mono); font-size: .76rem; color: var(--brand-muted);
    margin-bottom: .5rem; font-variant-numeric: tabular-nums; }
.picking-drill-count strong { color: var(--brand-navy); }
.picking-drill-rows { max-height: 340px; overflow-y: auto; }
/* Filas EPC del drill como rejilla: serial | empaque | origen | nota. */
.picking-epc-row { display: grid; grid-template-columns: 1.6fr 1fr 1.2fr 1fr; gap: .6rem; align-items: center;
    padding: .32rem .2rem; border-bottom: 1px solid var(--brand-line); font-size: .8rem; }
.picking-epc-row .s { font-family: var(--brand-font-mono); color: var(--brand-navy); font-weight: 500;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picking-epc-row .pu, .picking-epc-row .loc, .picking-epc-row .nt { color: var(--brand-body);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picking-epc-row.is-head { border-bottom: 1px solid var(--brand-line); font-size: .68rem; text-transform: uppercase;
    letter-spacing: .03em; color: var(--brand-muted); font-weight: 600; }
.picking-epc-row.is-head .s { font-family: inherit; color: var(--brand-muted); }
.picking-drill-empty, .picking-drill-loading { padding: 1rem .2rem; color: var(--brand-muted); font-size: .82rem; }
.picking-drill-loading::after { content: "…"; }

/* Paginador de líneas del detalle. */
.picking-pager { display: flex; align-items: center; justify-content: center; gap: .9rem; }
.picking-pager-btn { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px;
    border-radius: 8px; border: 1px solid var(--brand-line); background: var(--brand-paper); color: var(--brand-navy); }
.picking-pager-btn:hover { border-color: var(--brand-blue-deep); }
.picking-pager-btn.is-disabled { opacity: .4; pointer-events: none; }
.picking-pager-pos { font-family: var(--brand-font-mono); font-size: .82rem; color: var(--brand-muted);
    font-variant-numeric: tabular-nums; }

/* ============================================================
   Órdenes de trabajo — modelo matriz (asistente de pasos + detalle por asunto)
   ============================================================ */

/* Asistente: filas de pasos del procedimiento (instrucción + reordenar + eliminar) */
.wo-steps { margin-bottom: 4px; }
.wo-step-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.wo-step-num { flex: none; width: 26px; height: 26px; border-radius: 50%; display: inline-flex;
    align-items: center; justify-content: center; font-size: .78rem; font-weight: 600;
    background: var(--brand-blue-soft, #e8f0f7); color: var(--brand-blue-deep); font-variant-numeric: tabular-nums; }
.wo-step-input { flex: 1; }
.wo-step-controls { flex: none; display: inline-flex; align-items: center; gap: 2px; }
.wo-step-btn { flex: none; width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center;
    border: 0; background: transparent; color: var(--brand-muted); border-radius: 8px; cursor: pointer; padding: 0;
    font-size: 1.05rem; transition: background .12s ease, color .12s ease; }
.wo-step-btn:hover { background: var(--brand-wash, #eef2f6); color: var(--brand-navy); }
.wo-step-btn.is-danger:hover { background: #fbecea; color: #c0533b; }

/* Asistente: bloque de tareas POR ELEMENTO (cada elemento con su propio procedimiento) */
.wo-element { border: 1px solid var(--brand-line); border-radius: var(--border-radius-lg, 12px); padding: 14px 16px; margin-bottom: 12px; background: var(--brand-paper); }
.wo-element-head { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.wo-element-head .cell-id { flex: none; font-size: .92rem; }
.wo-element-head .wo-chip,
.wo-element-head .status-pill { flex: none; }
.wo-el-tasks { margin-bottom: 4px; }

/* Detalle: tarjeta de procedimiento (vista por paso) */
.wo-proc-step { display: flex; align-items: center; gap: 8px; padding: 6px 0; border-top: 1px solid var(--brand-line); }
.wo-proc-step:first-of-type { border-top: 0; }
.wo-proc-num { flex: none; width: 22px; height: 22px; border-radius: 50%; display: inline-flex;
    align-items: center; justify-content: center; font-size: .72rem; font-weight: 600;
    background: var(--brand-blue-soft, #e8f0f7); color: var(--brand-blue-deep); }
.wo-proc-instr { flex: 1; font-size: .86rem; color: var(--brand-navy); overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; }
.wo-proc-count { flex: none; font-size: .78rem; color: var(--brand-muted); font-variant-numeric: tabular-nums; }

/* Detalle: buscador de asuntos */
.wo-search { position: relative; }
.wo-search i { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--brand-muted); font-size: 1rem; }
.wo-search input { padding-left: 32px; width: 220px; max-width: 100%; }

/* Detalle: acordeón de asuntos (matriz por asunto) */
.wo-subject-head { display: flex; align-items: center; gap: 12px; padding: 14px 18px; cursor: pointer;
    user-select: none; }
.wo-subject-head .wo-caret { color: var(--brand-muted); transition: transform .15s ease; flex: none; }
.wo-subject-head[aria-expanded="true"] .wo-caret { transform: rotate(90deg); }
.wo-subject-head .cell-id { flex: none; }
.wo-chip { display: inline-flex; align-items: center; gap: 6px; font-size: .78rem; padding: 3px 10px;
    border-radius: 999px; background: var(--brand-wash, #f4f6f8); color: var(--brand-navy); }
.wo-chip i { font-size: .9rem; color: var(--brand-muted); }
.wo-subject-progress { margin-left: auto; flex: none; font-size: .8rem; color: var(--brand-muted);
    white-space: nowrap; font-variant-numeric: tabular-nums; }
.wo-subject-bar { flex: none; width: 96px; height: 6px; border-radius: 999px; background: var(--brand-line);
    overflow: hidden; }
.wo-subject-fill { display: block; height: 100%; border-radius: 999px; background: var(--brand-emerald, #17cc98); }
.wo-subject-body { padding: 4px 18px 14px 44px; border-top: 1px solid var(--brand-line); }
.wo-exec-row { display: flex; align-items: center; gap: 12px; padding: 7px 0; font-size: .86rem; }
.wo-exec-row + .wo-exec-row { border-top: 1px dashed var(--brand-line); }
.wo-exec-num { flex: none; width: 20px; color: var(--brand-muted); font-variant-numeric: tabular-nums; }
.wo-exec-instr { flex: 1; color: var(--brand-navy); }
.wo-exec-note { color: var(--brand-muted); }
.wo-exec-when { flex: none; min-width: 200px; text-align: right; }
.wo-exec-dates { display: flex; flex-direction: column; gap: 3px; align-items: flex-end; }
.wo-exec-date { display: block; font-size: .8rem; line-height: 1.35; white-space: nowrap; }
.wo-exec-date-label { font-size: .68rem; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
    color: var(--brand-muted); margin-right: 6px; }
.wo-exec-by { flex: none; min-width: 90px; }
/* Detalle de la orden: columnas de número, estado y fechas ajustadas a su contenido (antes estilos en línea). */
.work-order-detail-page .wo-exec-cell-fit { width: 1%; white-space: nowrap; }
.work-order-detail-page .wo-exec-duration { font-size: .9em; }

/* =====================================================================
   UTILIDADES — reglas de una línea usadas en múltiples vistas
   ===================================================================== */
.cursor-pointer { cursor: pointer; }

/* Subtitulo de seccion dentro de una tarjeta de detalle: mayusculas, negrita,
   tono oscuro, tamaño reducido. Generaliza el patron repetido con
   style="font-size: 0.85rem" + clases text-uppercase fw-bold text-dark en
   ~17 vistas de detalle (audit, business, inspection, lend, movement, etc.). */
.detail-subhead {
    text-transform: uppercase;
    font-weight: 700;
    color: var(--brand-ink);
    font-size: 0.85rem;
}

/* Detalle: medios y etiquetas  (reemplazan estilos inline de las vistas de detalle).
   Alturas de mapa/imagen unificadas para consistencia entre paginas de detalle. */
.detail-cover-img { width: 100%; height: 240px; object-fit: cover; }
.detail-map { width: 100%; height: 460px; }
.detail-avatar { width: 46px; height: 46px; }
.detail-label { font-size: 0.75rem; text-transform: uppercase; font-weight: 600; color: var(--brand-muted); }
.th-fixed { width: 80px; }

/* Estado vacio de listas/tarjetas: icono centrado + mensaje, con enlace
   opcional debajo. Generaliza el bloque "card-body text-center py-5" con
   icono ti-* fs-1 text-muted + parrafo fs-5 text-muted repetido en las
   vistas all.html de audit, business, category, country, inspection, etc. */
.empty-state {
    text-align: center;
    padding: 3rem 1rem;
}

.empty-state-icon {
    font-size: calc(1.375rem + 1.5vw);
    color: var(--brand-muted);
    margin-bottom: 0.5rem;
    display: block;
}

.empty-state-text {
    font-size: 1.25rem;
    color: var(--brand-muted);
    margin-bottom: 0;
}

/* =====================================================================
   LICENSING DETAIL — /licensing/{id}. Scoped bajo .licensing-detail-page.
   Movido desde el bloque <style> del template (DRY: estilos en CSS).
   ===================================================================== */
.licensing-detail-page .table > thead th {
    font-size: 0.85rem;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--brand-muted);
}
.licensing-detail-page .alert {
    border-radius: 0.5rem;
    font-weight: 500;
}

/* =====================================================================
   SENSOR DETAIL — /sensors/{id}. .filter-group es el overlay del mapa;
   scoped aquí para evitar colisión con .dashboard-page .filter-group.
   Movido desde el bloque <style> del template (DRY: estilos en CSS).
   ===================================================================== */
.sensor-detail-page .filter-group {
    font-family: var(--brand-font-sans);
    position: absolute;
    top: 15px;
    left: 15px;
    z-index: 5;
    background: rgba(255, 255, 255, 0.95);
    padding: 12px;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
    width: auto;
    max-width: 90%;
    color: var(--brand-ink);
}
.sensor-detail-page .filter-group label { font-size: 0.72rem; }

/* =====================================================================
   READER DETAIL — última geolocalización reportada por el dispositivo.
   Scoped bajo .reader-detail-page para no alterar otros mapas de detalle.
   ===================================================================== */
.reader-detail-page .reader-location-card {
    overflow: hidden;
}

.reader-detail-page .reader-location-layout {
    display: grid;
    grid-template-columns: minmax(0, 3fr) minmax(240px, 1fr);
    min-height: 360px;
}

.reader-detail-page .reader-location-map-shell {
    position: relative;
    min-height: 360px;
}

.reader-detail-page .reader-location-map,
.reader-detail-page .reader-location-map-state {
    width: 100%;
    min-height: 360px;
}

.reader-detail-page .reader-location-map-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    padding: 2rem;
    color: var(--brand-muted);
    background: var(--brand-paper);
    text-align: center;
}

.reader-detail-page .reader-location-map-state i {
    color: var(--brand-blue-deep);
    font-size: 2rem;
}

.reader-detail-page .reader-location-marker {
    color: var(--brand-blue-deep);
    cursor: default;
    filter: drop-shadow(0 3px 6px rgba(var(--brand-navy-rgb), 0.35));
    font-size: 2.5rem;
    line-height: 1;
}

.reader-detail-page .reader-location-facts {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 1.5rem;
    padding: 2rem;
    border-left: 1px solid var(--brand-line);
    background: var(--brand-white);
}

.reader-detail-page .reader-location-facts dt {
    color: var(--brand-muted);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.reader-detail-page .reader-location-facts dd {
    margin: 0.25rem 0 0;
    color: var(--brand-navy);
    font-weight: 600;
    overflow-wrap: anywhere;
}

@media (max-width: 767.98px) {
    .reader-detail-page .reader-location-layout {
        grid-template-columns: 1fr;
    }

    .reader-detail-page .reader-location-facts {
        border-top: 1px solid var(--brand-line);
        border-left: 0;
    }
}

/* =====================================================================
   AUDIT LIST — /audits. Select2 dentro de modales de reporte progresivo:
   ancho completo y dropdown anclado al modal (evita recorte/z-index).
   ===================================================================== */
.audit-list-page .modal .select2-container {
    width: 100% !important;
}

.audit-list-page .modal .select2-container--default .select2-selection--multiple {
    min-height: 2.5rem;
    border-color: var(--bs-border-color);
}

.audit-list-page .modal .select2-container--default.select2-container--focus .select2-selection--multiple {
    border-color: var(--brand-blue-deep);
    box-shadow: 0 0 0 0.2rem rgba(44, 90, 130, 0.15);
}

/* =====================================================================
   TIMELINE: lista vertical de eventos (historial de movimientos, prestamos).
   Estructura: punto + linea conectora + bloque de contenido (titulo/meta).
   ===================================================================== */
.timeline {
    list-style: none;
    margin: 0;
    padding: 0;
}

.timeline-item {
    position: relative;
    padding: 0 0 1.25rem 1.75rem;
}

.timeline-item:last-child {
    padding-bottom: 0;
}

/* conector vertical: una linea continua detras de todos los puntos */
.timeline-item::before {
    content: "";
    position: absolute;
    left: 5px;
    top: 4px;
    bottom: -1.25rem;
    width: 1px;
    background: var(--brand-line);
}

.timeline-item:last-child::before {
    display: none;
}

/* punto del evento, encima del conector */
.timeline-item::after {
    content: "";
    position: absolute;
    left: 0;
    top: 4px;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--brand-white);
    border: 2px solid var(--brand-blue-deep);
    z-index: 1;
}

.timeline-item.is-active::after {
    border-color: var(--brand-positive);
    background: var(--brand-positive);
}

.timeline-title {
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--brand-navy);
    margin-bottom: 0.15rem;
}

.timeline-meta {
    font-size: 0.75rem;
    color: var(--brand-muted);
}

/* =====================================================================
   FILTER CHIPS: filtros activos como pildoras removibles, mas el contador
   del boton que abre el panel de filtros.
   ===================================================================== */
.filter-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.4rem 0.3rem 0.7rem;
    border-radius: 999px;
    background: var(--brand-navy-tint);
    color: var(--brand-navy);
    font-size: 0.78rem;
    font-weight: 500;
    border: 1px solid var(--brand-navy-tint-border);
}

.filter-chip-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.1rem;
    height: 1.1rem;
    border: 0;
    border-radius: 50%;
    padding: 0;
    background: transparent;
    color: var(--brand-blue-deep);
    font-size: 0.7rem;
    line-height: 1;
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
}

.filter-chip-remove:hover {
    background: var(--brand-white);
    color: var(--brand-navy);
}

/* contador pequeño sobre el boton "Filtros" cuando hay filtros activos */
.filter-count-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.1rem;
    height: 1.1rem;
    padding: 0 0.3rem;
    border-radius: 999px;
    background: var(--brand-blue-deep);
    color: var(--brand-white);
    font-size: 0.65rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

/* =====================================================================
   SORT INDICATORS: flechas de orden en encabezados de tabla (solo CSS,
   el toggle asc/desc lo controla JS agregando la clase correspondiente).
   ===================================================================== */
.th-sort {
    position: relative;
    cursor: pointer;
    padding-right: 1.1rem !important;
    user-select: none;
}

.th-sort::after {
    content: "\2195";
    position: absolute;
    right: 0.35rem;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.75em;
    color: var(--brand-line);
}

.th-sort.asc::after {
    content: "\2191";
    color: var(--brand-navy);
}

.th-sort.desc::after {
    content: "\2193";
    color: var(--brand-navy);
}

/* =====================================================================
   TOASTS: notificaciones flotantes (contrato de nombres con otro paquete,
   no renombrar). Pila fija abajo-derecha, tarjeta con acento de color segun
   variante, entrada animada (respeta prefers-reduced-motion).
   ===================================================================== */
.trackvy-toast-container {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 1080;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    width: 320px;
    max-width: calc(100vw - 2.5rem);
}

.trackvy-toast {
    display: flex;
    align-items: flex-start;
    gap: 0.7rem;
    padding: 0.85rem 1rem;
    border-radius: var(--border-radius);
    background: var(--brand-white);
    border: 1px solid var(--brand-line);
    border-left: 4px solid var(--brand-blue-deep);
    box-shadow: 0 12px 30px rgba(var(--brand-navy-rgb), 0.14);
    font-size: 0.85rem;
    color: var(--brand-body);
    animation: trackvy-toast-in 0.2s ease;
}

.trackvy-toast.is-success { border-left-color: var(--brand-positive); }
.trackvy-toast.is-error { border-left-color: var(--brand-danger); }
.trackvy-toast.is-info { border-left-color: var(--brand-blue-deep); }

.trackvy-toast-icon {
    flex: none;
    font-size: 1.1rem;
    line-height: 1.2;
    color: var(--brand-blue-deep);
}

.trackvy-toast.is-success .trackvy-toast-icon { color: var(--brand-positive); }
.trackvy-toast.is-error .trackvy-toast-icon { color: var(--brand-danger); }

.trackvy-toast-body { flex: 1; min-width: 0; }
.trackvy-toast-title { font-weight: 600; color: var(--brand-ink); margin-bottom: 0.1rem; }
.trackvy-toast-message { color: var(--brand-muted); }

@keyframes trackvy-toast-in {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
}

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


.select2-container {
    width: 100% !important;
}

/* =====================================================================
   PRESENCIA — /analytics/presence. Todo va bajo .presence-page.
   Tablero de personas por sede y zona: tarjeta por sede, filas de zona
   sangradas por profundidad, y la ultima lectura como dato protagonista.
   La barra de cada zona reutiliza la primitiva .track/.fill (ver BARRAS
   DE PROGRESO) y las pestanas de estado reutilizan las de picking.
   ===================================================================== */

.presence-page .presence-updated { font-family: var(--brand-font-mono); font-size: .78rem; color: var(--brand-muted); }

.presence-page .presence-filter { min-width: 190px; }
.presence-page .presence-filters .eyebrow { font-size: .62rem; }

/* KPI: solo dos cifras se tinen; el resto queda navy para no gritar */
.presence-page .kpi-figure.is-inside { color: var(--brand-positive); }
.presence-page .kpi-figure.is-unconfirmed { color: var(--brand-warn-text); }

/* ---- Tarjeta de sede + filas de zona ---- */
.presence-page .presence-site-head {
    display: flex; align-items: center; gap: .6rem; padding: .9rem 1.1rem; text-decoration: none;
    border-bottom: 1px solid var(--brand-line); color: var(--brand-ink);
}
.presence-page .presence-site-head:hover { background: var(--brand-paper); }
.presence-page .presence-site-head.is-selected { background: var(--brand-navy-tint); }
.presence-page .presence-site-name { font-weight: 600; font-size: .95rem; }

.presence-page .presence-zone {
    display: flex; align-items: center; gap: .6rem; padding: .55rem 1.1rem; text-decoration: none;
    border-bottom: 1px solid var(--brand-line); color: var(--brand-body); font-size: .85rem;
}
.presence-page .presence-zone:last-child { border-bottom: 0; }
.presence-page .presence-zone:hover { background: var(--brand-paper); color: var(--brand-navy); }
.presence-page .presence-zone.is-selected { background: var(--brand-navy-tint); color: var(--brand-navy); font-weight: 500; }
.presence-page .presence-zone.is-empty { color: var(--brand-muted); }
.presence-page .presence-zone-name { flex: 1; }

/* Sangria por profundidad: el arbol se lee de un vistazo sin estilos en linea */
.presence-page .presence-zone.is-depth-1 { padding-left: 1.5rem; }
.presence-page .presence-zone.is-depth-2 { padding-left: 2.6rem; }
.presence-page .presence-zone.is-depth-3 { padding-left: 3.7rem; }
.presence-page .presence-zone.is-depth-4 { padding-left: 4.8rem; }

/* Fila plana del resumen (ranking de zonas ocupadas) y enlace "y N zonas mas" */
.presence-page .presence-zone.is-flat { padding-left: 1.5rem; }
.presence-page .presence-zone.is-more { color: var(--brand-blue-deep); font-size: .8rem; }
.presence-page .presence-zone.is-more i { font-size: .9rem; }

.presence-page .presence-readers {
    display: inline-flex; align-items: center; gap: .2rem; font-size: .72rem; color: var(--brand-muted);
    border: 1px solid var(--brand-line); border-radius: 999px; padding: .05rem .45rem;
}
.presence-page .presence-count {
    min-width: 2.2rem; text-align: right; font-weight: 600; color: var(--brand-navy); font-size: .9rem;
}
.presence-page .presence-bar { display: flex; align-items: center; width: 64px; }
.presence-page .presence-bar .fill { transition: width .8s cubic-bezier(.4, 0, .2, 1); }

.presence-page .presence-empty-sites { font-size: .8rem; color: var(--brand-muted); margin: -.4rem 0 1.4rem; }
.presence-page .presence-roster-title {
    display: flex; align-items: center; gap: .4rem; font-weight: 600; color: var(--brand-ink); margin-bottom: .8rem;
}

/* ---- Roster: ubicacion, antiguedad de la lectura y motivo ---- */
.presence-page .presence-place { display: flex; flex-direction: column; line-height: 1.25; }
.presence-page .presence-place-site { color: var(--brand-ink); }
.presence-page .presence-place-zone { font-size: .78rem; color: var(--brand-muted); }
.presence-page .presence-ago { display: block; font-size: .75rem; color: var(--brand-muted); }
.presence-page .presence-reason { display: block; font-size: .72rem; color: var(--brand-muted); margin-top: .15rem; }

/* ---- Diagnostico y alcance del dato ---- */
.presence-page .presence-diagnostics {
    display: flex; align-items: center; flex-wrap: wrap; gap: .5rem 1.1rem; margin-top: 1.4rem;
    padding: .7rem 1rem; border: 1px solid var(--brand-line); border-radius: var(--border-radius);
    background: var(--brand-paper); font-size: .82rem; color: var(--brand-body);
}
.presence-page .presence-diagnostics .is-conflict { color: var(--brand-warn-text); font-weight: 500; }
.presence-page .presence-disclaimer { margin-top: .9rem; font-size: .78rem; color: var(--brand-muted); }

/* ---- Modal "Inspeccionar": lista de sedes/zonas con casilla y conteo Dentro ---- */
.presence-page .presence-inspect-tree {
    max-height: 320px; overflow-y: auto; border: 1px solid var(--brand-line);
    border-radius: var(--border-radius); padding: .3rem 0;
}
.presence-page .presence-inspect-row {
    display: flex; align-items: center; gap: .6rem; padding: .45rem 1rem; margin: 0; cursor: pointer;
}
.presence-page .presence-inspect-row:hover { background: var(--brand-paper); }
.presence-page .presence-inspect-row.is-zone { padding-left: 2.2rem; font-size: .85rem; color: var(--brand-body); }
.presence-page .presence-inspect-row.is-site { font-weight: 600; color: var(--brand-ink); }
.presence-page .presence-inspect-name { flex: 1; }
.presence-page .presence-inspect-count { min-width: 2rem; text-align: right; color: var(--brand-navy); }


/* =====================================================================
   NOTIFICACIONES — avisos IDI (.idi-uplink-*), campana del encabezado
   (.notif-*) y centro de actividad (.activity-page).
   La campana permanece discreta, con un numero del tamano del que usa el
   correo sin leer. Los avisos IDI aparecen solo al recibir una lectura.
   Ninguno usa color de alarma en reposo y todo movimiento respeta
   prefers-reduced-motion.
   ===================================================================== */

/* ---- Avisos en vivo del lector IDI ---- */
.idi-uplink-notifications {
    position: fixed;
    top: 4.5rem;
    right: 1.25rem;
    z-index: 1080;
    display: flex;
    flex-direction: column;
    gap: .65rem;
    width: 22.5rem;
    max-width: calc(100vw - 2.5rem);
}

.idi-uplink-notification {
    position: relative;
    display: grid;
    grid-template-columns: 2rem minmax(0, 1fr);
    gap: .75rem;
    padding: .9rem 2.75rem .9rem .95rem;
    color: var(--brand-body);
    background: var(--brand-white);
    border: 1px solid var(--brand-line);
    border-left: 4px solid var(--brand-blue-deep);
    border-radius: var(--border-radius);
    box-shadow: 0 12px 30px rgba(var(--brand-navy-rgb), .14);
    animation: idi-uplink-notification-in .2s ease;
}

.idi-uplink-notification-icon {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    border-radius: var(--border-radius);
    color: var(--brand-blue-deep);
    background: var(--brand-navy-tint);
    font-size: 1.05rem;
}

.idi-uplink-notification-body { min-width: 0; }

.idi-uplink-notification-summary {
    margin: 0;
    color: var(--brand-ink);
    font-size: .84rem;
    font-weight: 600;
    line-height: 1.4;
}

.idi-uplink-notification-close {
    position: absolute;
    top: .65rem;
    right: .65rem;
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    color: var(--brand-muted);
    background: transparent;
    border: 0;
    border-radius: var(--border-radius);
    font-size: 1rem;
    line-height: 1;
}

.idi-uplink-notification-close:hover {
    color: var(--brand-blue-deep);
    background: var(--brand-paper);
}

.idi-uplink-notification-tags {
    max-height: 14rem;
    margin: .55rem 0 0;
    padding: 0;
    overflow-y: auto;
    list-style: none;
}

.idi-uplink-notification-tags:not(.is-expanded) li:nth-child(n+2) { display: none; }

.idi-uplink-notification-tags li {
    padding: .38rem .5rem;
    overflow-wrap: anywhere;
    color: var(--brand-body);
    background: var(--brand-paper);
    border: 1px solid var(--brand-line-soft);
    border-radius: var(--border-radius);
    font-family: var(--brand-font-mono);
    font-size: .72rem;
    line-height: 1.35;
}

.idi-uplink-notification-tags li + li { margin-top: .3rem; }

.idi-uplink-notification-toggle {
    margin-top: .55rem;
    padding: 0;
    color: var(--brand-blue-deep);
    background: transparent;
    border: 0;
    font-size: .76rem;
    font-weight: 600;
}

.idi-uplink-notification-toggle:hover { color: var(--brand-blue-deep-600); }

.idi-uplink-notification-close:focus-visible,
.idi-uplink-notification-toggle:focus-visible {
    outline: 2px solid var(--brand-blue-deep);
    outline-offset: 2px;
}

@keyframes idi-uplink-notification-in {
    from { opacity: 0; transform: translateY(-.4rem); }
    to { opacity: 1; transform: translateY(0); }
}

@media (max-width: 575.98px) {
    .idi-uplink-notifications {
        top: 4.25rem;
        right: .75rem;
        width: calc(100vw - 1.5rem);
        max-width: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .idi-uplink-notification { animation: none; }
}

/* ---- La campana y su numero ---- */
.notif-bell .pc-head-link { position: relative; }

.notif-badge {
    position: absolute;
    top: .3rem;
    right: .25rem;
    min-width: 1.05rem;
    padding: 0 .28rem;
    border-radius: 999px;
    background: var(--brand-blue-deep);
    color: var(--brand-white);
    font-family: var(--brand-font-mono);
    font-size: .62rem;
    font-weight: 600;
    line-height: 1.05rem;
    text-align: center;
    animation: notif-badge-in .18s ease;
}

@keyframes notif-badge-in {
    from { opacity: 0; transform: scale(.7); }
    to { opacity: 1; transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .notif-badge { animation: none; }
}

/* ---- El desplegable ---- */
.notif-menu { width: 360px; max-width: calc(100vw - 1.5rem); padding: 0; }

.notif-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: .75rem .95rem .6rem; border-bottom: 1px solid var(--brand-line);
}

.notif-icon-btn {
    border: 0; background: none; padding: .1rem .2rem; line-height: 1;
    color: var(--brand-muted); font-size: 1rem; border-radius: var(--border-radius);
}
.notif-icon-btn:hover { color: var(--brand-blue-deep); background: var(--brand-paper); }

.notif-panel { max-height: 22rem; overflow-y: auto; }

.pc-header .notif-list { display: block; width: 100%; margin: 0; }

.notif-item { border-bottom: 1px solid var(--brand-line-soft); }
.notif-item:last-child { border-bottom: 0; }

.notif-item-body {
    display: block; padding: .6rem .95rem .6rem 1.25rem; position: relative;
    color: var(--brand-body); text-decoration: none;
}
a.notif-item-body:hover { background: var(--brand-paper); text-decoration: none; }

/* El punto de severidad: neutro por omision, y el rojo reservado para el error de verdad. */
.notif-item-body::before {
    content: ""; position: absolute; left: .6rem; top: .95rem;
    width: .35rem; height: .35rem; border-radius: 50%; background: var(--brand-pill-neutral-border);
}
.notif-item.is-warning .notif-item-body::before { background: var(--brand-warning); }
.notif-item.is-error .notif-item-body::before { background: var(--brand-danger); }
.notif-item.is-unseen .notif-item-body::before { background: var(--brand-blue-deep); }

.notif-item-title {
    display: block; font-size: .82rem; font-weight: 500; color: var(--brand-ink);
}
.notif-item.is-unseen .notif-item-title { font-weight: 600; }

.notif-item-count {
    margin-left: .35rem; font-family: var(--brand-font-mono); font-size: .7rem;
    font-weight: 500; color: var(--brand-muted);
}

.notif-item-detail {
    display: block; font-size: .76rem; color: var(--brand-body); margin-top: .1rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.notif-item-meta { display: block; font-size: .7rem; color: var(--brand-muted); margin-top: .2rem; }

.notif-empty { padding: 1.6rem .95rem; margin: 0; text-align: center; font-size: .8rem; color: var(--brand-muted); }

/* ---- Preferencias ---- */
.notif-prefs { padding: .8rem .95rem 1rem; }
.notif-prefs .form-check { margin-bottom: .35rem; }
.notif-prefs .form-check-label { font-size: .82rem; color: var(--brand-body); }
.notif-prefs-eyebrow { display: block; margin: .9rem 0 .4rem; }

.notif-back {
    display: inline-flex; align-items: center; gap: .35rem; border: 0; background: none;
    padding: 0 0 .6rem; font-size: .78rem; color: var(--brand-blue-deep);
}
.notif-back:hover { color: var(--brand-blue-deep-600); }

.notif-hint { margin: .7rem 0 0; font-size: .72rem; color: var(--brand-muted); }

/* ---- Pie ---- */
.notif-foot {
    display: flex; align-items: center; justify-content: space-between;
    padding: .55rem .95rem; border-top: 1px solid var(--brand-line); background: var(--brand-paper);
    font-size: .78rem;
}
.notif-foot a { color: var(--brand-blue-deep); text-decoration: none; }
.notif-foot a:hover { text-decoration: underline; }

.notif-link-btn { border: 0; background: none; padding: 0; font-size: .78rem; color: var(--brand-muted); }
.notif-link-btn:hover { color: var(--brand-blue-deep); }

/* ---- Centro de actividad (/analytics/activity) ---- */
.activity-page .activity-day { margin-bottom: 1.8rem; }
.activity-page .activity-day-label {
    display: block; margin-bottom: .7rem; padding-bottom: .4rem; border-bottom: 1px solid var(--brand-line);
}

.activity-page .activity-row {
    display: flex; align-items: flex-start; gap: .9rem; padding: .8rem .2rem;
    border-bottom: 1px solid var(--brand-line-soft);
}
.activity-page .activity-row:last-child { border-bottom: 0; }

.activity-page .activity-time {
    flex: none; width: 4.2rem; font-family: var(--brand-font-mono); font-size: .75rem;
    color: var(--brand-muted); padding-top: .1rem;
}
.activity-page .activity-main { flex: 1; min-width: 0; }
.activity-page .activity-title { font-weight: 500; color: var(--brand-ink); }
.activity-page .activity-detail { display: block; font-size: .82rem; color: var(--brand-body); margin-top: .15rem; }
.activity-page .activity-meta { display: flex; align-items: center; gap: .5rem; margin-top: .35rem; }
.activity-page .activity-count { font-family: var(--brand-font-mono); font-size: .72rem; color: var(--brand-muted); }
.activity-page .activity-empty { padding: 3rem 1rem; text-align: center; color: var(--brand-muted); }
.activity-page .activity-empty i { font-size: 2rem; display: block; margin-bottom: .6rem; opacity: .6; }
.activity-page .activity-note { margin-top: 1.6rem; font-size: .78rem; color: var(--brand-muted); }

/* ===== MODULOS DE CLIENTE (views/module/detail.html) =====
   Los modulos de cliente no traen CSS: pintan HTML con clases de Bootstrap y de
   Trackvy dentro de [data-trackvy-module]. Aqui vive solo lo que ese HTML no puede decidir solo: el ritmo vertical
   entre bloques, sin margenes propios del modulo, y el estilo de los dos elementos nativos que usan, <progress>
   (mismo trazo de 6px en navy que la familia BARRAS DE PROGRESO) y <caption> (como eyebrow, arriba). */
.module-host-page [data-trackvy-module] :is(section, form:not(.row)) > * + * { margin-top: 1rem; }
.module-host-page [data-trackvy-module] :is(p, .alert) { margin-bottom: 0; }
.module-host-page [data-trackvy-module] .form-text { display: block; margin-top: .35rem; color: var(--brand-muted); }
.module-host-page [data-trackvy-module] [role="status"] { display: flex; flex-direction: column; gap: .4rem; }
.module-host-page [data-trackvy-module] progress {
    width: 100%; height: 6px; border: 0; border-radius: 999px; overflow: hidden;
    appearance: none; background: var(--brand-line-soft); accent-color: var(--brand-navy);
}
.module-host-page [data-trackvy-module] progress::-webkit-progress-bar { background: var(--brand-line-soft); }
.module-host-page [data-trackvy-module] progress::-webkit-progress-value { background: var(--brand-navy); border-radius: 999px; }
.module-host-page [data-trackvy-module] progress::-moz-progress-bar { background: var(--brand-navy); border-radius: 999px; }
.module-host-page [data-trackvy-module] caption { caption-side: top; padding: 0 0 .5rem; color: var(--brand-muted); }

/* ===== GENERACION DE ETIQUETAS (views/tag/generation.html) =====
   Tabla de filas empaque, ubicacion, estado y cantidad. La barra de cupo usa el track + fill compartido
   de BARRAS DE PROGRESO; aqui solo va lo propio de la pagina. */
.tag-generation-page .generation-toolbar,
.tag-generation-page .generation-totals { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .75rem 1.5rem; }
.tag-generation-page .generation-totals { border-top: 1px solid var(--brand-line); background: var(--brand-paper); }
.tag-generation-page .generation-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.tag-generation-page .generation-tip { color: var(--brand-muted); cursor: help; margin-left: .25rem; }
.tag-generation-page .generation-col-code { min-width: 18rem; }
.tag-generation-page .generation-col-location { min-width: 14rem; }
.tag-generation-page .generation-col-state { min-width: 10rem; }
.tag-generation-page .generation-col-quantity { min-width: 8.5rem; width: 8.5rem; }
.tag-generation-page .generation-review { min-width: 12rem; white-space: normal; }
.tag-generation-page .generation-review .status-pill { margin: .125rem .25rem .125rem 0; }
.tag-generation-page .generation-empty { margin: 0; padding: 1rem 1.25rem; }
.tag-generation-page .generation-meter { display: inline-flex; align-items: center; gap: .75rem; min-width: 16rem; }
.tag-generation-page .generation-meter .track { min-width: 8rem; }
.tag-generation-page .generation-meter .fill.is-over { background: var(--brand-warning); }

/* ===== DATA STRIP (fragments/kpi/strip.html) =====
   One thin, always-one-row KPI header shared by the tag, packing-unit, reference and sensor list pages. --strip-row is the row's fixed height, built from the three lines' own
   explicit line-heights below (label 14px + figure 26px + hint 16px, plus the cell's vertical
   padding), never from a guessed pixel count, so no label, figure or hint is ever clipped
   mid-letter. Una sola fila a todo ancho: cuando las celdas no caben, la fila se desplaza de lado y
   ninguna celda queda cortada ni escondida. */
.strip-name { display: flex; align-items: baseline; justify-content: flex-end; gap: .6rem; font-size: .8125rem; font-weight: 600; color: var(--brand-navy); margin-bottom: .4rem; }
.strip-name a,
.strip-name .strip-asof { font-size: .75rem; font-weight: 500; color: var(--brand-body); text-decoration: none; }
.strip-name a:hover,
.strip-name a:focus-visible { color: var(--brand-blue-deep); }
.strip-name a:hover { text-decoration: underline; }
.strip-name .info { font-size: .8rem; }

/* Una página con pocas cifras lleva una franja compacta a la derecha, bajo la fecha del snapshot, que crece
   hacia la izquierda con cada celda. Una franja de seis o más celdas (el dashboard) llena la fila; en una
   pantalla más angosta la fila se desplaza de lado en vez de apretar u ocultar celdas. */
.strip-block { width: fit-content; max-width: 100%; margin-left: auto; }
.strip-block:has(.strip-cell:nth-child(6)) { width: 100%; }
/* A full-width strip spreads its cells evenly to the edge on a wide screen instead of stopping short. */
.strip-block:has(.strip-cell:nth-child(6)) .strip-cell { max-width: none; }
.strip { display: flex; background: var(--brand-white); border: 1px solid var(--brand-line); border-radius: var(--border-radius); overflow: hidden; }
.strip { --strip-row: calc(14px + 26px + 16px + 2 * .7rem); }

/* Una fila que se desplaza de lado sin barra visible; el borde derecho se desvanece mientras quedan celdas por
   ver (.has-more-right, js/common/list-head.js). */
.strip-cells { flex: 1; min-width: 0; display: flex; flex-wrap: nowrap; height: var(--strip-row); overflow-x: auto; overflow-y: hidden; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.strip-cells::-webkit-scrollbar { display: none; }

.strip-cell {
    position: relative;                              /* anchors the .stretched-link overlay */
    flex: 1 0 auto;                                  /* nunca más angosta que su contenido; reparte el sobrante */
    min-width: 104px;
    max-width: 240px;
    height: var(--strip-row);
    padding: .7rem .875rem;
    display: grid;
    grid-template-rows: 14px 26px 16px;
    row-gap: 2px;
    align-content: start;
    border-left: 1px solid var(--brand-line);
}
.strip-cell:first-child { border-left: 0; }
@media (max-width: 575.98px) {
    /* Phones: the strip keeps its single row and scrolls sideways with a swipe, so every figure is
       reachable instead of only the first one fitting. */
    .strip-block { width: 100%; }
    /* Width from the content with a floor, so a long hint is not cut at a fixed 150px. */
    .strip-cell { flex: 0 0 auto; min-width: 150px; max-width: none; scroll-snap-align: start; }
    /* The vertical-ellipsis link is invisible until the swipe reaches it; every cell already opens
       the snapshot and the snapshot link above the strip stays visible. */
    .strip a.strip-more { display: none; }
}
/* Right-edge fade while cells are still hidden (js/common/list-head.js sets .has-more-right). */
.strip-cells.has-more-right,
.strip.is-thin.has-more-right {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
}
@media (min-width: 576px) {
    /* La franja delgada suelta celdas en pantalla ancha en vez de desplazarse, así que no lleva pista.
       La franja completa se desplaza en todo ancho y conserva su pista. */
    .strip.is-thin.has-more-right { -webkit-mask-image: none; mask-image: none; }
}
.strip-cell.is-linked:hover { background: var(--brand-canvas); }
.strip-cell.lead.is-linked:hover { background: var(--brand-navy-tint-border); }

.strip-cell .k { line-height: 14px; font-family: var(--brand-font-mono); font-size: .625rem; font-weight: 500; letter-spacing: .1em; text-transform: uppercase; color: var(--brand-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.strip-cell .v { line-height: 26px; font-family: var(--brand-font-mono); font-size: 1.25rem; font-weight: 500; letter-spacing: -0.01em; color: var(--brand-navy); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.strip-cell .h { line-height: 16px; font-size: .72rem; color: var(--brand-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.strip-cell .h.is-ok { color: var(--brand-positive-text); }
.strip-cell .h.is-warn { color: var(--brand-warn-text); }

/* Lead cell: the entity's own total, always first, always the navy brand tone. Muted text on the
   dark fill reuses the same tokens as .entity-hero (login/entity-header navy surfaces) instead of a
   one-off hex. */
/* The lead cell reads as a light navy tint, not a solid navy block. */
.strip-cell.lead { background: var(--brand-navy-tint); }
.strip-cell.lead .k { color: var(--brand-blue-deep); }
.strip-cell.lead .v { color: var(--brand-navy); }
.strip-cell.lead .h { color: var(--brand-muted); }
/* A hint with a tooltip sits above the cell's stretched link, or the link would swallow the hover.
   Keyed on the toggle, not on title: Bootstrap moves the title away on the first hover. */
.strip-cell .h[data-bs-toggle] { position: relative; z-index: 2; cursor: help; }

.strip-more { flex: 0 0 36px; display: grid; place-items: center; border-left: 1px solid var(--brand-line); color: var(--brand-blue-deep); text-decoration: none; font-size: 1.25rem; line-height: 1; }
.strip-more:hover { background: var(--brand-canvas); }

/* THIN DATA STRIP (fragments/kpi/strip.html :: kpiStripThin): the same cells on one line at the
   height of the page's buttons, at the left of a list page's actions row.
   .list-head is that row: strip on the left, actions on the right, always one row on a desktop. */
.list-head { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; margin-bottom: 1.5rem; }
.list-head-actions { display: flex; align-items: center; gap: .5rem; margin-left: auto; flex: none; }
/* Nunca una cifra cortada a la mitad: si la franja no alcanza un ancho legible junto a las
   acciones, baja a su propia linea (la fila envuelve) en vez de encogerse. */
.strip.is-thin { --strip-row: 36px; height: 36px; max-width: 100%; min-width: min(100%, 16rem); }
.strip.is-thin .strip-cell {
    display: flex; align-items: center; gap: .45rem;
    flex: 0 0 auto; min-width: 0; max-width: none; height: 36px;
    padding: 0 .75rem; line-height: 36px; white-space: nowrap;
}
.strip.is-thin .strip-cell .k,
.strip.is-thin .strip-cell .v,
.strip.is-thin .strip-cell .h { line-height: 36px; }
/* A thin cell is as wide as its content, so its label and figure are never clipped with an ellipsis. */
.strip.is-thin .strip-cell .k,
.strip.is-thin .strip-cell .v { overflow: visible; text-overflow: clip; }
.strip.is-thin .strip-cell .v { font-size: .9375rem; }
/* When the strip and the actions do not fit one row, the strip keeps label and figure and the hint
   becomes the cell's tooltip (js/common/list-head.js), so the row never splits in two. */
.list-head.is-compact .strip.is-thin .strip-cell .h { display: none; }
.list-head.is-compact .strip.is-thin .strip-cell.is-dropped { display: none; }
.strip.is-thin .strip-more { flex-basis: 30px; font-size: 1rem; }
@media (max-width: 575.98px) {
    /* Phones: the thin strip takes its own line and scrolls sideways, like the full strip. */
    .list-head { flex-wrap: wrap; }
    .strip.is-thin { width: 100%; overflow-x: auto; scrollbar-width: none; }
    .strip.is-thin::-webkit-scrollbar { display: none; }
    /* Actions take the full row, wrap, and share it in equal cells. */
    .list-head-actions { width: 100%; flex: 1 1 100%; flex-wrap: wrap; margin-left: 0; }
    /* Una accion por fila: los rotulos reales ("Descargar referencias", "Crear referencia") no
       caben en media pantalla y partirlos en dos lineas rompe el boton desplegable. */
    .list-head-actions > * { flex: 1 1 100%; min-width: 0; }
    .list-head-actions > .btn,
    .list-head-actions .dropdown > .btn { width: 100%; justify-content: center; }
}

/* ===== BUSCAR ANTES DE CREAR (js/common/utils.js initSelect2Ajax con options.create, y el
   diálogo de alta de ubicación en js/common/dialogs.js) =====
   La opción "Crear «X»" del picker de categoría/palabra clave y la lista de ubicaciones "parecidas"
   comparten el mismo lenguaje: una fila por resultado, con una nota muted a la derecha. */
.find-or-create-option { display: flex; align-items: center; justify-content: space-between; gap: .75rem; width: 100%; }
.find-or-create-option .muted { color: var(--brand-muted); font-size: .82rem; white-space: nowrap; }

.loc-similar-list { margin: .75rem 0 0; padding: .75rem; border: 1px solid var(--brand-line); border-radius: .5rem; background: var(--brand-paper); }
.loc-similar-list .loc-similar-title { display: block; font-size: .78rem; font-weight: 600; color: var(--brand-muted); margin-bottom: .5rem; }
.loc-similar-row { display: flex; align-items: center; justify-content: space-between; gap: .75rem; padding: .35rem 0; border-top: 1px solid var(--brand-line-soft); }
.loc-similar-row:first-of-type { border-top: 0; }
.loc-similar-row .loc-similar-name { color: var(--brand-ink); font-weight: 500; }
.loc-similar-row .loc-similar-open { color: var(--brand-blue-deep); font-size: .82rem; font-weight: 500; text-decoration: none; }
.loc-similar-row .loc-similar-open:hover { text-decoration: underline; }

/* ===== IMAGE DIALOG (fragments/image/dialog.html, js/common/image-dialog.js, js/common/image-shaper.js) =====
   Shared "pick or drop an image" modal: a 400 x 400 preview
   stage, fit/background/source segmented controls (buttons share the width evenly, no uneven pills) and the
   compact trigger + thumbnail a form field shows once an image is chosen. Instructions live in the existing
   .info icon + native title attribute, never as page text; no new tooltip class needed. */
.image-dialog-grid { display: grid; grid-template-columns: 15rem minmax(0, 1fr); gap: 1.25rem; }
@media (max-width: 640px) { .image-dialog-grid { grid-template-columns: 1fr; } }
.image-dialog-side { display: flex; flex-direction: column; gap: .6rem; }

.image-dialog-stage {
    width: 100%; aspect-ratio: 1; border-radius: .6rem; border: 1px solid var(--brand-line);
    display: grid; place-items: center; overflow: hidden;
    background-image: linear-gradient(45deg, var(--brand-line-soft) 25%, transparent 25%),
        linear-gradient(-45deg, var(--brand-line-soft) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, var(--brand-line-soft) 75%),
        linear-gradient(-45deg, transparent 75%, var(--brand-line-soft) 75%);
    background-size: 16px 16px; background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}
.image-dialog-stage canvas, .image-dialog-stage img { width: 100%; height: 100%; object-fit: contain; display: block; }
.image-dialog-empty { font-size: .8rem; color: var(--brand-muted); text-align: center; padding: 1rem; }

.image-dialog-seg-label {
    font-family: var(--brand-font-mono); font-size: .68rem; letter-spacing: .07em; text-transform: uppercase;
    color: var(--brand-muted);
}
.image-dialog-seg { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; width: 100%; border: 1px solid var(--brand-line); border-radius: .5rem; overflow: hidden; }
.image-dialog-seg button {
    border: 0; border-left: 1px solid var(--brand-line); background: var(--brand-white); padding: .45rem .5rem;
    font-size: .8rem; color: var(--brand-muted); cursor: pointer; text-align: center;
}
.image-dialog-seg button:first-child { border-left: 0; }
.image-dialog-seg button[aria-pressed="true"] { background: var(--brand-navy); color: var(--brand-white); }

.image-dialog-drop { border: 1.5px dashed var(--brand-line-soft); border-radius: .6rem; padding: .9rem; display: grid; gap: .4rem; text-align: center; font-size: .8rem; color: var(--brand-muted); }
.image-dialog-drop.is-drag-over { border-color: var(--brand-blue-deep); background: var(--brand-navy-tint); }
.image-dialog-drop input { font-size: .78rem; max-width: 100%; }
.image-dialog-lbl { display: block; font-size: .8rem; color: var(--brand-body); margin-bottom: .25rem; }
.image-dialog-field .form-control { font-size: .85rem; }

.image-dialog-facts { display: grid; grid-template-columns: auto 1fr; gap: .25rem .75rem; font-size: .82rem; margin: 0; }
.image-dialog-facts dt { color: var(--brand-muted); }
.image-dialog-facts dd { margin: 0; color: var(--brand-navy); font-variant-numeric: tabular-nums; }

.image-dialog-weight { border-radius: .5rem; padding: .55rem .7rem; font-size: .8rem; }
.image-dialog-weight.is-ok { background: var(--brand-positive-tint); color: var(--brand-positive-text); }
.image-dialog-weight.is-warn { background: var(--brand-warn-tint); color: var(--brand-warn-text); }
.image-dialog-error { font-size: .8rem; }

/* The field as it sits in a form (reference_form.html): a trigger button, then the thumbnail once
   something is picked. The real file/link inputs stay in the DOM (data-image-field-file/-link) so the
   controller's existing multipart field name never changes; only their visibility is JS-driven. */
.image-field { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; }
.image-field-picked { display: flex; align-items: center; gap: .6rem; }
.image-field-thumb { width: 2.5rem; height: 2.5rem; border-radius: .4rem; border: 1px solid var(--brand-line); object-fit: contain; background: var(--brand-paper); }
.image-field-meta { font-size: .78rem; color: var(--brand-muted); max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* =====================================================================
   ADMINISTRACION Y MENU: grupos del submenu de
   Configuracion, la matriz de permisos de roles y el diagrama de la
   maquina de estados. Un solo bloque anadido al final, por convencion.
   ===================================================================== */

/* Configuracion > grupos (Catalogos, Dispositivos e integracion, Cuenta): una fila de
   encabezado ligera dentro del propio pc-submenu, no el .pc-caption de nivel superior
   (ese esta pensado para el ancho completo del sidebar, no para un submenu anidado). */
/* Same voice as the main captions (colour, weight, no capitals), one step smaller and indented to the
   submenu's own icons (its links pad 58px), so a group reads as part of its submenu. */
.pc-submenu-caption { pointer-events: none; padding: .75rem 1rem .25rem 58px; margin-top: .25rem; }
.pc-submenu-caption label {
    font-size: .6875rem; font-weight: 500; line-height: 1.66; letter-spacing: normal; text-transform: none;
    color: var(--pc-sidebar-caption-color); margin: 0;
}

/* ---- Matriz de permisos (/roles/{id}) ---- */
.role-matrix-strip .strip-cell.lead .h { color: var(--brand-muted); }
.role-matrix-filters .role-matrix-area-filter { flex: 1 1 220px; min-width: 180px; }
.role-matrix-col-filter { width: auto; min-width: 8.5rem; }
/* Telefono: busqueda a lo ancho y los cuatro filtros de columna en una cuadricula pareja de 2x2. */
@media (max-width: 575.98px) {
    .role-matrix-filters .role-matrix-area-filter { flex: 1 1 100%; }
    .role-matrix-filters .role-matrix-col-filter { flex: 1 1 calc(50% - .25rem); min-width: 0; }
}
.role-matrix-scroll { max-height: calc(100vh - 320px); max-height: calc(100dvh - 320px); }
.role-matrix th.text-center { text-align: center; }
.role-matrix-col-head { display: flex; flex-direction: column; align-items: center; gap: .35rem; }
.role-matrix-cell { white-space: nowrap; }
.role-matrix-cell.na { color: var(--brand-line-soft); background: var(--brand-paper); }
.role-matrix-special { white-space: normal; min-width: 220px; }
.role-matrix-special-pills { display: flex; flex-wrap: wrap; gap: .35rem; }
.role-matrix-pill {
    display: inline-flex; align-items: center; gap: .35rem; font-size: .74rem;
    padding: .2rem .55rem; border-radius: 999px; background: var(--brand-navy-tint);
    color: var(--brand-navy); border: 1px solid var(--brand-navy-tint-border); cursor: pointer;
}
.role-matrix-row.is-hidden { display: none; }
.role-matrix-checkbox { cursor: pointer; }

/* ---- Diagrama de la maquina de estados (/state-machine) ---- */
.state-machine-diagram { width: 100%; height: 420px; }
.state-machine-filter { max-width: 22rem; }
.state-machine-row { cursor: pointer; }
.state-machine-row.is-highlighted-row { background: var(--brand-navy-tint); }
.state-machine-row.is-hidden { display: none; }

/* ================= EMPAQUES Y REFERENCIAS =================
   Un solo libro de reglas (PackingUnitRules) del lado servidor; aqui solo el vestido de sus
   prechecks en vivo y los presets del empaque de identidad. */

/* Codigo + chip de disponibilidad (references/new y packing-units/new): "Disponible" en verde
   calmo, "Ya existe" en ambar de aviso (nunca rojo: no es una accion destructiva). */
.code-check-field { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; }
/* The validation message jQuery Validate inserts after the input takes its own line. */
.code-check-field > .error:not(input) { flex-basis: 100%; margin-top: -.35rem; }
.code-check-field .form-control { flex: 1 1 auto; }
.code-check-field .status-pill { flex: 0 0 auto; white-space: nowrap; }
#code-check-state { margin-top: .25rem; }
#code-check-state a { font-weight: 600; }

/* Peso + unidad (reference Empaques y packing-unit forms): la unidad va SIEMPRE junto al numero,
   limitada a kg/g/lb/oz por el controlador (measurementUnitService.findWeightUnits). */
.weight-field { display: grid; grid-template-columns: 1fr 5.5rem; gap: .5rem; }

/* El empaque de identidad no se elige, se muestra: siempre existe, con sus presets fijados al
   crear la referencia (R8/R9). */
.identity-preset .presets,
.identity-preset .row { row-gap: .75rem; }

/* ===== IMAGE FALLBACK thumbnails (reference/all.html): tamaños como clases; js/common/image-fallback.js apunta el data-fallback-src a este mismo
   recorte. The detail pages show their picture in .detail-media (DETAIL VIEW). */
.reference-thumb-sm { width: 35px; height: 35px; object-fit: cover; }

/* ===== DASHBOARD (main dashboard from the snapshot) =====
   Every dashboard box opens its live view: card titles are links. The "Niveles y
   flujos" table and pie's own rules live in the DASHBOARD: NIVELES Y FLUJOS section at the end of
   this file. */
.dashboard-page .dash-card-link { text-decoration: none; }
.dashboard-page .dash-card-link:hover { text-decoration: underline; }

/* ===== MISSING HERE, SEEN THERE (§5c U4 / U1) =====
   The audit/inspection missing tabs, the surplus-pairing note, the tag detail's recent-reads panel
   and the clickable coverage-card KPIs on /analytics/overview. */

/* Summary line + filter chips at the top of the missing tab, and the pairing note on the surplus tab. */
.seen-elsewhere-summary .card-body { padding: .85rem 1.1rem; }
.seen-elsewhere-tip,
.missing-as-of-today-tip { color: var(--brand-muted); cursor: help; margin-left: .2rem; font-size: .85rem; }
.seen-filter-row { align-items: center; }

/* Filter chips reuse .status-pill (calm, no alarm color) as a clickable toggle: text-decoration
   reset, a hover tint and a bold border for the active chip so the state reads at a glance. */
.seen-filter-chip { text-decoration: none; cursor: pointer; transition: filter .15s ease; }
.seen-filter-chip:hover { filter: brightness(0.97); }
.seen-filter-chip.is-active { border-width: 2px; font-weight: 700; }

/* "Últimas lecturas en campo" panel on the tag detail page. */
.recent-field-reads { display: grid; gap: .65rem; }
.recent-field-read-row { padding-bottom: .65rem; border-bottom: 1px solid var(--brand-line-soft); }
.recent-field-read-row:last-child { border-bottom: 0; padding-bottom: 0; }

/* U1: the coverage-card KPIs and tier rows become links; reuse the existing static .kpi look, only
   add the pointer and a very small tint on hover (still no movement, per brand: KPI cards are static). */
.kpi-link { display: block; text-decoration: none; color: inherit; cursor: pointer; border-radius: .4rem; }
.kpi-link:hover { background: var(--brand-navy-tint); }
.audit-tier-link { text-decoration: none; cursor: pointer; transition: filter .15s ease; }
.audit-tier-link:hover { filter: brightness(0.97); }

/* A small muted "help" icon next to a strip-cell label or an inline warning,
   for a tooltip that explains a figure  without filling the page with a help note. */
.strip-cell-tip { color: var(--brand-muted); cursor: help; margin-left: .2rem; font-size: .85rem; }

/* ===== DASHBOARD: NIVELES Y FLUJOS =====
   The "Niveles y flujos" table (state, Hace/Entradas/Salidas/Hoy plus a 30-day inflow sparkline) and
   the "Etiquetas por estado" pie beside it. Page-scoped under .dashboard-page, brand tokens only. */
.dashboard-page .levels-flow-scroll { overflow-x: auto; max-width: 100%; }
/* Fixed layout so the table always fits its card: the state name takes what it needs, the four
   figures share evenly, and the daily bars take the rest and shrink with it. */
.dashboard-page .levels-flow-table { table-layout: fixed; width: 100%; min-width: 560px; }
.dashboard-page .levels-flow-table > thead > tr > th,
.dashboard-page .levels-flow-table > tbody > tr > td { padding-left: .75rem; padding-right: .75rem; }
.dashboard-page .levels-flow-table > thead > tr > th { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dashboard-page .levels-flow-table > thead > tr > th:first-child { width: 16%; }
.dashboard-page .levels-flow-table > thead > tr > th:nth-child(n+2):nth-child(-n+5) { width: 13%; }
/* + and − carry one glyph as a header, so they take less width than Inicio and Hoy. */
.dashboard-page .levels-flow-table > thead > tr > th:nth-child(n+3):nth-child(-n+4) { width: 10%; }
.dashboard-page .levels-flow-table .levels-time-col { width: 19%; }
/* A header that is its own tooltip (no info icon beside it) shows the help cursor. The + and −
   headers are single glyphs, sized up to read at the header's small caps size. */
.dashboard-page .levels-flow-table .levels-sign,
.dashboard-page .levels-flow-table .levels-tip { cursor: help; }
.dashboard-page .levels-flow-table .levels-sign { font-size: .9375rem; font-weight: 600; line-height: 1; }
.dashboard-page .levels-flow-table .levels-bars-col { width: 19%; }
.dashboard-page .levels-flow-table > thead > tr > th { padding-left: .5rem; padding-right: .5rem; letter-spacing: .04em; vertical-align: bottom; }
@media (max-width: 767.98px) {
    /* Phones: the four figures stay, the daily bars go; the table fits without scrolling. */
    .dashboard-page .levels-flow-table { min-width: 0; }
    .dashboard-page .levels-flow-table .levels-bars-col,
    .dashboard-page .levels-flow-table .levels-time-col { display: none; }
    .dashboard-page .levels-flow-table > thead > tr > th:first-child { width: 22%; }
    .dashboard-page .levels-flow-table > thead > tr > th:nth-child(n+2):nth-child(-n+5) { width: 19.5%; }
    /* Fixed layout clips what does not fit: figures stay on one line and the last header ("Hoy")
       keeps its full width instead of being cut by the card edge. */
    .dashboard-page .levels-flow-table > tbody > tr > td.num { white-space: nowrap; overflow: visible; }
    .dashboard-page .levels-flow-table > thead > tr > th:nth-child(5) { overflow: visible; }
    .dashboard-page .levels-flow-table > thead > tr > th,
    .dashboard-page .levels-flow-table > tbody > tr > td { padding-left: .4rem; padding-right: .4rem; }
    /* Narrow columns: headers wrap onto two lines instead of being clipped. */
    .dashboard-page .levels-flow-table > thead > tr > th { white-space: normal; overflow: visible; text-overflow: clip; vertical-align: bottom; line-height: 1.2; word-break: keep-all; overflow-wrap: normal; hyphens: none; font-size: .625rem; letter-spacing: .02em; }
}
/* La tarjeta mide su propio ancho: angosta (al lado de la IA o de Ultimos movimientos en una pantalla media) suelta
   primero la tendencia y luego el tiempo promedio, en vez de mostrar una barra de desplazamiento (Matt, 2026-11-09). */
.dashboard-page #dashboard-levels { container-type: inline-size; container-name: levels; }
@container levels (max-width: 680px) {
    .dashboard-page .levels-flow-table { min-width: 0; }
    .dashboard-page .levels-flow-table .levels-bars-col { display: none; }
}
@container levels (max-width: 520px) {
    .dashboard-page .levels-flow-table .levels-time-col { display: none; }
}
.dashboard-page .levels-total-row > td { font-weight: 700; border-top: 2px solid var(--brand-line); color: var(--brand-navy); }

/* One small bar per day of the current window, tallest bar scaled to the row's own maximum so every
   row reads at its own comparable scale; the last bar (the snapshot day) is the accent color, every
   other bar stays a calm neutral (status color is never alarm color). */
/* The bars share the cell's width equally whatever their number (7, 30 or 90), and the cell clips what could
   spill. The window class sets the gap: wide for a week, hairline for a month, none for a quarter, where each
   bar is about two pixels. */
.dashboard-page .levels-bars-col { min-width: 0; overflow: hidden; }
.dashboard-page .levels-bars { --levels-bar-gap: 1px; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); grid-template-rows: 100%; align-items: end; gap: var(--levels-bar-gap); height: 28px; min-width: 0; }
.dashboard-page .levels-bars.is-window-7 { --levels-bar-gap: .25rem; }
.dashboard-page .levels-bars.is-window-90 { --levels-bar-gap: 0; }
.dashboard-page .levels-bar { min-width: 0; min-height: 2px; background: var(--brand-navy-tint-border); border-radius: 1px 1px 0 0; }
.dashboard-page .levels-bars.is-window-7 .levels-bar { max-width: 1.25rem; justify-self: center; width: 100%; }
.dashboard-page .levels-bar.is-today { background: var(--brand-blue-deep); }

/* ===== DASHBOARD: INICIO OPERATIVO (Matt, 2026-11-09) =====
   Una sola pagina para toda empresa. La franja llena la fila; bajo ella, el reparto por estado. Movimientos apila cada
   dia o semana por el estado al que paso la etiqueta, con los tonos .seg-N de las paginas de detalle (los mismos
   --state-* del pie). Ubicaciones es una tabla con una columna por estado presente. Ultimos movimientos y Conteos
   recientes comparten la lista .home-feed. th:style y el script solo ponen anchos y altos calculados. */
.dashboard-page.operator-home .strip-block { width: 100%; }
.dashboard-page.operator-home .strip-cell { max-width: none; }
/* La celda Movimientos espera la respuesta de 30 dias de la tarjeta: nunca muestra un cero que no es. */
.dashboard-page [data-live-hook="movements-30d"]:not(.is-filled) { visibility: hidden; }

.dashboard-page .home-states { margin: -.25rem 0 .25rem; }
.dashboard-page .home-states-bar { display: flex; height: 6px; border-radius: 3px; overflow: hidden; background: var(--brand-line); }
.dashboard-page .home-states-legend { display: flex; flex-wrap: wrap; gap: .25rem 1.25rem; list-style: none; padding: 0; margin: .5rem 0 0; font-size: .78rem; color: var(--brand-muted); }
.dashboard-page .home-states-legend li,
.dashboard-page .home-states-legend a,
.dashboard-page .home-states-legend li > span { display: inline-flex; align-items: center; gap: .4rem; color: inherit; }
.dashboard-page .home-states-legend b { color: var(--brand-ink); font-weight: 600; }

.dashboard-page .places-swatch { display: inline-block; flex: none; width: .6rem; height: .6rem; border-radius: 2px; }
.dashboard-page .places-seg { display: block; height: 100%; min-width: 0; }
.dashboard-page .places-table th .places-swatch { margin-right: .3rem; vertical-align: baseline; }
/* Hasta nueve columnas en cualquier ancho sin desplazarse: el nombre toma un cuarto y se recorta, los encabezados
   pueden partirse y las cifras reparten el resto. */
.dashboard-page .places-table { table-layout: fixed; width: 100%; }
.dashboard-page .places-table th:first-child { width: 26%; }
.dashboard-page .places-table th { white-space: normal; line-height: 1.25; vertical-align: bottom; }
.dashboard-page .places-table th,
.dashboard-page .places-table td { padding-left: .5rem; padding-right: .5rem; }
.dashboard-page .places-table td { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dashboard-page .places-table td a { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.dashboard-page .movements-state { display: flex; align-items: center; justify-content: center; min-height: 200px; font-size: .875rem; }
.dashboard-page .movements-chart { display: flex; flex-direction: column; flex: 1; }
.dashboard-page .movements-legend { margin: 0 0 .75rem; }
.dashboard-page .movements-plot { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); grid-template-rows: 100%; align-items: end; gap: 3px; height: 190px; border-bottom: 1px solid var(--brand-line); }
.dashboard-page .movements-plot.is-count-7 { gap: 1.25rem; padding: 0 .5rem; }
.dashboard-page .movements-col { display: flex; flex-direction: column-reverse; min-width: 0; border-radius: 3px 3px 0 0; overflow: hidden; }
.dashboard-page .movements-col.is-current { opacity: .4; }
.dashboard-page .movements-part { display: block; flex-basis: 0; min-height: 1px; }
.dashboard-page .movements-axis { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 3px; margin-top: .35rem; }
.dashboard-page .movements-axis.is-count-7 { gap: 1.25rem; padding: 0 .5rem; }
.dashboard-page .movements-tick { font-size: .6875rem; color: var(--brand-muted); text-align: center; white-space: nowrap; overflow: visible; }
.dashboard-page .movements-facts { display: flex; flex-wrap: wrap; gap: .75rem 2.5rem; margin-top: 1rem; padding-top: .9rem; border-top: 1px solid var(--brand-line-soft); }
.dashboard-page .movements-facts > div { display: flex; flex-direction: column; font-size: .75rem; color: var(--brand-muted); }
.dashboard-page .movements-facts b { font-size: 1.15rem; font-weight: 600; line-height: 1.3; color: var(--brand-ink); }

.dashboard-page .home-feed { list-style: none; padding: 0; margin: 0; }
.dashboard-page .home-feed li { display: grid; grid-template-columns: .5rem minmax(0, 1fr) auto; gap: .1rem .65rem; padding: .55rem 0; border-bottom: 1px solid var(--brand-line-soft); }
.dashboard-page .home-feed li:last-child { border-bottom: 0; }
/* A todo el ancho, la lista corre en columnas: cada entrada conserva su linea inferior. */
.dashboard-page .home-feed.is-wide { display: grid; grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); column-gap: 2rem; }
.dashboard-page .home-feed.is-wide li:last-child { border-bottom: 1px solid var(--brand-line-soft); }
.dashboard-page .home-feed-dot { width: .5rem; height: .5rem; border-radius: 50%; margin-top: .4rem; }
.dashboard-page .home-feed-name { font-size: .8125rem; color: var(--brand-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dashboard-page .home-feed-when { font-size: .75rem; color: var(--brand-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.dashboard-page .home-feed-meta { grid-column: 2 / 4; font-size: .75rem; color: var(--brand-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.dashboard-page .home-map-card .map-shell { height: 420px; }
/* Mapbox pone su contenedor en position: relative y su alto en 100% no resuelve; anclado al marco, toma sus 420px. */
.dashboard-page .home-map-card #map { position: absolute; inset: 0; }
@media (max-width: 575.98px) {
    .dashboard-page .dashboard-movements .card-header .range-switch { width: 100%; }
    .dashboard-page .dashboard-movements .card-header .range-switch a { flex: 1 1 0; text-align: center; }
    .dashboard-page .movements-plot { height: 150px; }
    .dashboard-page .movements-plot.is-count-7,
    .dashboard-page .movements-axis.is-count-7 { gap: .5rem; padding: 0; }
}

/* ===== PRESENCIA › ELEMENTOS (views/presence/items.html) =====
   Location reconciliation. The routes card reads as "registered > seen: tags"; the per-location card
   scrolls inside itself so a tenant with hundreds of locations keeps the list below in view. */
.items-page .items-routes td { padding-top: .6rem; padding-bottom: .6rem; }
.items-page .items-route-from,
.items-page .items-route-to { max-width: 12rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.items-page .items-route-sep { width: 1.5rem; color: var(--brand-muted); text-align: center; }
.items-page .items-locations-scroll { max-height: 26rem; overflow-y: auto; }
.items-page .items-locations-scroll { overflow-x: hidden; }
/* Fixed columns so the table fits its card: the name and the last-audit cell wrap, the figures stay narrow. */
.items-page .items-locations { table-layout: fixed; width: 100%; }
.items-page .items-locations > thead > tr > th,
.items-page .items-locations > tbody > tr > td { padding-left: .5rem; padding-right: .5rem; }
.items-page .items-locations > thead > tr > th { white-space: normal; word-break: keep-all; overflow-wrap: normal; vertical-align: bottom; line-height: 1.2; font-size: .625rem; letter-spacing: .02em; }
.items-page .items-locations > thead > tr > th:first-child { width: 24%; }
.items-page .items-locations > thead > tr > th:nth-child(n+2):nth-child(-n+5) { width: 13%; }
.items-page .items-locations > thead > tr > th:last-child { width: 24%; }
.items-page .items-locations > tbody > tr > td { white-space: normal; overflow-wrap: anywhere; }
.items-page .items-description { font-size: .75rem; color: var(--brand-muted); }

/* ===== STICKY HOVER ON TOUCH SCREENS =====
   iOS keeps :hover on the last tapped element, so an outline button stayed filled grey after a tap.
   Where the device cannot hover, the hover variables repeat the resting ones; the pressed (:active)
   fill is untouched. Desktop keeps the fill through the untouched Bootstrap rules. */
@media (hover: none) {
    [data-pc-preset="preset-1"] .btn[class*="btn-outline-"] {
        --bs-btn-hover-color: var(--bs-btn-color);
        --bs-btn-hover-bg: transparent;
        --bs-btn-hover-border-color: var(--bs-btn-border-color);
    }
}

/* Sidebar opens without animation on page load: layout.html drops .is-sidebar-preload once the page has loaded,
   so the current group appears already open instead of the caret turning on every navigation. */
.is-sidebar-preload .pc-sidebar,
.is-sidebar-preload .pc-sidebar * {
    transition: none !important;
}

/* =====================================================================
   ALERTAS > RETORNABLES (/alert/returnables)
   Siete columnas con celdas sin salto de linea desbordaban la tarjeta a 1600 px y la ultima columna
   (el estado del prestamo) quedaba oculta tras el scroll horizontal. Relleno lateral mas estrecho y
   persona y ubicacion pueden partir linea, asi el estado cabe completo; en pantallas mas angostas
   sigue el scroll de .grid-scroll con sus sombras.
   ===================================================================== */
.returnables-page .table-airy > thead > tr > th,
.returnables-page .table-airy > tbody > tr > td { padding-left: .75rem; padding-right: .75rem; }
.returnables-page .table-airy > tbody > tr > td.is-wrap { white-space: normal; min-width: 9rem; max-width: 16rem; }
.returnables-page .table-airy .status-pill { white-space: nowrap; }

/* ===== CONTEOS CON PROBLEMAS (views/audit_count/all.html, views/audit_count/problem_summary.html) =====
   Las pastillas de problema reutilizan .status-pill.is-warn (aviso calmo, nunca color de alarma) y solo
   anaden el icono a la izquierda; las tablas de totales por usuario y por dispositivo usan .table-airy. */
/* Status cell: the transmission pill and one icon-only pill per problem, packed on one line. */
.audit-count-log-page .audit-count-status { display: flex; align-items: center; gap: .25rem; white-space: nowrap; }
.audit-count-log-page .problem-pill { display: inline-flex; align-items: center; text-decoration: none; }
.audit-count-log-page .problem-totals > thead > tr > th { white-space: nowrap; }
.audit-count-log-page .problem-totals .cell-name { max-width: 14rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* =====================================================================
   DETAIL VIEW (fragments/detail.html, js/common/detail-tabs.js)
   One shell for the detail pages (tag, packing unit, reference, sensor; the other detail views move later):
     1. detail :: identity   breadcrumb ending in the code, thumb, name, pills; actions on the right
     2. detail :: card       one full-width card of line tabs, General first, Edit last
     3. General = cover      picture and facts beside the strip and the panels. Four layouts:
                               default          side | figures (strip + panels)
                               .is-compact      a strip but no panels (sensor): strip first, then side
                               .is-facts-only   no figure the viewer may see: picture and facts only
                               .is-single-item  the reference or packing unit holds one tag: the item instead
   General loads with the page; every other tab loads on its first view and never again.
   .detail-shell is a size container, so the layout follows the content width (sidebar open or closed). It is
   also the containing block of fixed descendants, so modals stay outside it. Computed widths arrive as --pct
   through th:style (the computed-value carve-out).
   ===================================================================== */
.detail-shell { container: detail / inline-size; }

/* 1. Identity bar: replaces the left header card and the separate title row. */
.detail-identity-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem 1.25rem; margin: 0 0 1.25rem; }
.detail-identity { display: flex; align-items: center; gap: .875rem; min-width: 0; flex: 1 1 22rem; }
.detail-titles { min-width: 0; }
.detail-crumbs { margin: 0 0 .15rem; font-size: .75rem; }
/* The last crumb is the code of a reference or packing unit; tag and sensor end in the noun instead. */
.detail-crumbs .breadcrumb-item.is-code { font-family: var(--brand-font-mono); }
.detail-name-row { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .5rem; min-width: 0; }
.detail-name { margin: 0; min-width: 0; font-size: 1.3rem; font-weight: 600; line-height: 1.3; color: var(--brand-ink); overflow-wrap: anywhere; }
/* Tag and sensor: the name is the EPC serial or the DevEUI. */
.detail-name.is-mono { font-family: var(--brand-font-mono); font-size: 1.125rem; font-weight: 500; letter-spacing: .01em; }
.detail-meta { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
.detail-meta a.status-pill { text-decoration: none; }
.detail-meta a.status-pill:hover { border-color: var(--brand-navy-tint-border); color: var(--brand-navy); }
.detail-actions { display: flex; align-items: center; gap: .5rem; margin-left: auto; flex: none; }
.detail-actions > form { margin: 0; }
/* A disabled action keeps its tooltip: .btn.disabled drops pointer events, so the title sits on a wrapper. */
.detail-action-tip { display: inline-flex; cursor: not-allowed; }
.detail-action-tip > .btn { pointer-events: none; }

/* 2. The card: line tabs with a quiet count; Edit pushed to the end, neutral. */
.detail-card { background: var(--brand-white); }
.detail-card > .card-header { padding: 0 1.25rem; background: var(--brand-white); }
.detail-tabs { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
.detail-tabs .nav-link { display: inline-flex; align-items: center; gap: .4rem; white-space: nowrap; padding: 13px 16px; }
.detail-tabs .nav-item.is-end { margin-left: auto; }
/* A count renders only when it is free: the model, or the tag_count read the page already made. */
.tab-count {
    font-size: .6875rem; font-weight: 600; line-height: 1.45; font-variant-numeric: tabular-nums;
    padding: 0 .42rem; border-radius: 999px;
    color: var(--brand-muted); background: var(--brand-paper); border: 1px solid var(--brand-line);
}
.detail-tabs .nav-link.active .tab-count { color: var(--brand-navy); background: var(--brand-navy-tint); border-color: var(--brand-navy-tint-border); }
.detail-card .tab-pane { padding: 1.5rem; }

/* 3. General, the cover. */
.detail-cover { display: grid; grid-template-columns: minmax(240px, 300px) minmax(0, 1fr); gap: 2rem; align-items: start; }
.detail-cover-side { min-width: 0; }
.detail-figures { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.25rem; align-content: start; }
/* The kpiStrip fragment, unchanged, runs the full width of its column. */
.detail-figures .strip-block { width: 100%; margin: 0 !important; }
.detail-figures .strip-block .strip-cell { max-width: none; }
.detail-cover.is-compact { grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
.detail-cover.is-compact .detail-figures { order: -1; }
.detail-cover.is-facts-only { grid-template-columns: minmax(0, 1fr); }
.detail-cover.is-compact .detail-cover-side.has-media,
.detail-cover.is-facts-only .detail-cover-side.has-media { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 1.75rem; align-items: start; }
.detail-cover.is-compact .detail-facts,
.detail-cover.is-facts-only .detail-facts { grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); margin-top: 0; }

/* The picture, or the entity's glyph when there is none. The picture is changed from the Edit tab. */
.detail-media {
    display: grid; place-items: center; width: 100%; aspect-ratio: 4 / 3; overflow: hidden;
    border: 1px solid var(--brand-line); border-radius: var(--border-radius); background: var(--brand-paper);
}
.detail-media img { width: 82%; height: 82%; object-fit: contain; }
.detail-media.is-empty { color: var(--brand-muted); font-size: 2.5rem; }

/* Facts: a definition grid with hairlines. Smart fields are ordinary facts; created and updated are quiet. */
.detail-facts { display: grid; grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr)); gap: 0 1.25rem; margin: 1.25rem 0 0; }
.detail-facts > div { min-width: 0; padding: .6rem 0 .7rem; border-top: 1px solid var(--brand-line-soft); }
.detail-facts > .is-wide { grid-column: 1 / -1; }
.detail-facts dt {
    display: flex; align-items: center; gap: .3rem; margin-bottom: .15rem;
    font-size: .6875rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--brand-muted);
}
.detail-facts dd { margin: 0; font-size: .9375rem; font-weight: 500; color: var(--brand-ink); overflow-wrap: anywhere; }
.detail-facts dd.is-quiet { font-weight: 400; font-size: .875rem; color: var(--brand-body); }
.detail-facts dd.is-mono { font-family: var(--brand-font-mono); font-size: .875rem; }
.detail-facts dd a { color: var(--brand-blue-deep); text-decoration: none; }
.detail-facts dd a:hover { text-decoration: underline; }

/* Panels: two columns beside the side column; one column on a narrow container. */
.detail-panels { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
.detail-panel { min-width: 0; padding: 1rem 1.1rem 1.1rem; border: 1px solid var(--brand-line); border-radius: var(--border-radius); }
.detail-panel.is-wide { grid-column: 1 / -1; }
.detail-panel-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .85rem; }
.detail-panel-head .eyebrow { display: inline-flex; align-items: center; gap: .35rem; }
.detail-panel-head .eyebrow .info { letter-spacing: 0; }
.detail-panel-head .eyebrow > .ph-duotone:not(.info) { font-size: .95rem; letter-spacing: 0; color: var(--brand-navy); }

/* State tones, one class per state from the STATE COLOURS tokens: .seg-1 Inventory, .seg-2 Lent, .seg-3 In
   transit, .seg-4 Retired, .seg-5 a tenant state, .seg-6 Initial. The dashboard donut reads the same tokens. */
.seg-1 { background: var(--state-inventory); }
.seg-2 { background: var(--state-lent); }
.seg-3 { background: var(--state-transition); }
.seg-4 { background: var(--state-retired); }
.seg-5 { background: var(--state-other); }
.seg-6 { background: var(--state-initial); }

/* detail :: statePanel. One 100% bar plus an exact legend. */
.state-split { display: flex; gap: 2px; height: 12px; border-radius: 999px; overflow: hidden; background: var(--brand-line-soft); }
.state-split > span { display: block; height: 100%; width: var(--pct, 0%); min-width: 3px; }
.state-legend { list-style: none; padding: 0; margin: .85rem 0 0; display: grid; gap: .4rem; }
.state-legend li { display: grid; grid-template-columns: 8px minmax(0, 1fr) auto 3.4rem; align-items: center; gap: .55rem; font-size: .8125rem; color: var(--brand-body); }
.state-legend .num { color: var(--brand-navy); font-weight: 600; text-align: right; }
.state-legend .pct { color: var(--brand-muted); font-size: .75rem; text-align: right; }
.state-dot { width: 8px; height: 8px; border-radius: 50%; }

/* detail :: ladderPanel. Name and figure on the first line, then a bar whose length is the row's size against
   the largest row and whose segments are its state mix. Names link to the filtered list. */
.dist-ladder { display: grid; gap: .7rem; }
.dist-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "name val" "bar bar"; gap: .25rem .75rem; align-items: baseline; }
.dist-name { grid-area: name; min-width: 0; font-size: .8125rem; font-weight: 500; color: var(--brand-body); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-decoration: none; }
a.dist-name { color: var(--brand-blue-deep); }
a.dist-name:hover { text-decoration: underline; }
.dist-val { grid-area: val; font-size: .8125rem; font-weight: 600; color: var(--brand-navy); }
.dist-val .pct { margin-left: .35rem; font-weight: 400; color: var(--brand-muted); font-size: .75rem; }
.dist-track { grid-area: bar; display: block; height: 8px; border-radius: 999px; background: var(--brand-paper); overflow: hidden; }
.dist-bar { display: flex; gap: 1px; height: 100%; width: var(--pct, 0%); min-width: 3px; border-radius: 999px; overflow: hidden; }
.dist-bar > span { display: block; height: 100%; width: var(--pct, 0%); }
.dist-row.is-rest .dist-name { color: var(--brand-muted); }
.dist-row.is-rest .dist-bar { opacity: .55; }

/* detail :: levelPanel. Units in stock against the reference's own minimum and maximum; facts, no advice. */
.level-band { position: relative; height: 12px; margin: 1.6rem 0 .6rem; border-radius: 999px; background: var(--brand-line); }
.level-fill { display: block; height: 100%; width: var(--pct, 0%); max-width: 100%; border-radius: 999px; background: var(--brand-navy); }
.level-mark { position: absolute; top: -6px; bottom: -6px; left: var(--pct, 0%); width: 1px; background: var(--brand-muted); }
.level-mark-label { position: absolute; top: -1.4rem; transform: translateX(-50%); font-size: .6875rem; color: var(--brand-muted); white-space: nowrap; }
.level-mark.is-end .level-mark-label { transform: translateX(-100%); }
.level-foot { display: flex; flex-wrap: wrap; justify-content: space-between; gap: .25rem .75rem; font-size: .78rem; color: var(--brand-muted); }
.level-foot .num { color: var(--brand-navy); font-weight: 600; }

/* A short list inside a panel (contents per tag, units per packing unit). */
.detail-list { list-style: none; padding: 0; margin: 0; display: grid; }
.detail-list > li { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; padding: .5rem 0; border-top: 1px solid var(--brand-line-soft); font-size: .8125rem; color: var(--brand-body); }
.detail-list > li:first-child { border-top: 0; padding-top: 0; }
.detail-list .num { color: var(--brand-navy); font-weight: 600; }

/* The single-item cover: the item where the strip and the panels would be. Every figure is the item's own, so
   the block is gated by the tag's location scope, not by READ_ANALYTIC_REPORTS. */
.detail-item { gap: 1.25rem; }
.detail-item-head { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .75rem; padding-bottom: 1rem; border-bottom: 1px solid var(--brand-line); }
.detail-item-head > .eyebrow { flex-basis: 100%; }
.detail-item-serial {
    min-width: 0; font-family: var(--brand-font-mono); font-size: 1.05rem; font-weight: 500; letter-spacing: .01em;
    color: var(--brand-navy); text-decoration: none; overflow-wrap: anywhere;
}
.detail-item-serial:hover { color: var(--brand-blue-deep); text-decoration: underline; }
.detail-item-since { margin-left: auto; font-size: .8125rem; color: var(--brand-muted); }
.detail-item-lead { margin: 0 0 .35rem; font-size: 1rem; font-weight: 600; line-height: 1.35; color: var(--brand-ink); overflow-wrap: anywhere; }
.detail-item-lead a { color: var(--brand-blue-deep); text-decoration: none; }
.detail-item-lead a:hover { text-decoration: underline; }
.detail-item-lead.is-none { font-weight: 500; color: var(--brand-muted); }
.detail-panel > .detail-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; }
.detail-panel > .detail-facts > div { padding: .5rem 0 .55rem; }

/* Container widths. 940 px of content is a 1280 px laptop with the sidebar open. */
@container detail (max-width: 939.98px) {
    .detail-cover { grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
    .detail-cover-side { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 1.5rem; align-items: start; }
    .detail-cover-side:not(:has(.detail-media)) { grid-template-columns: minmax(0, 1fr); }
    .detail-cover-side .detail-facts { margin-top: 0; }
}
@container detail (max-width: 639.98px) {
    .detail-identity { flex-basis: 100%; }
    .detail-actions { width: 100%; margin-left: 0; flex-wrap: wrap; }
    .detail-actions > * { flex: 1 1 0; min-width: 0; }
    .detail-actions .btn { width: 100%; justify-content: center; }
    /* Four actions (tag: lend, move, reports, delete) share two rows of two. */
    .detail-actions:has(> :nth-child(4)) > * { flex: 1 1 calc(50% - .25rem); }
    .detail-card > .card-header { padding: 0 .25rem; }
    .detail-tabs .nav-link { padding: 12px 12px; }
    .detail-card .tab-pane { padding: 1rem; }
    .detail-cover-side,
    .detail-cover.is-compact .detail-cover-side.has-media,
    .detail-cover.is-facts-only .detail-cover-side.has-media { grid-template-columns: minmax(0, 1fr); }
    .detail-media { max-width: 260px; }
    .detail-facts,
    .detail-cover.is-compact .detail-facts,
    .detail-cover.is-facts-only .detail-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .detail-panels { grid-template-columns: minmax(0, 1fr); }
    .detail-item-since { flex-basis: 100%; margin-left: 0; }
    /* The strip keeps one row and scrolls sideways, as the viewport rule already does on a phone. */
    .detail-figures .strip-cells { flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x proximity; scrollbar-width: none; }
    .detail-figures .strip-cell { flex: 0 0 auto; min-width: 150px; max-width: none; scroll-snap-align: start; }
    .detail-figures .strip a.strip-more { display: none; }
}
/* The QR dialog of the tag (Reports, QR code): the existing .tag-qr box, centred. */
.tag-qr-dialog .modal-body { display: grid; justify-items: center; gap: .75rem; padding: 1.5rem; }
.tag-qr-dialog .tag-qr { width: 196px; }

/* =====================================================================
   ANALYTICS DASHBOARD (views/analytics/overview.html)
   The cards sit on a grid that follows its own width (sidebar open or closed). Each .dash-row shares its width
   equally among the cards present: the stock now (tags by state, deployment age, loans), the range (movements,
   composition) and operations (receipts, audits). A card whose section is null renders nothing, so its row
   closes up; a row with no card disappears.
   ===================================================================== */
.analytics-shell { container: analytics / inline-size; }
.analytics-dashboard { display: grid; gap: 1rem; }
.analytics-dashboard > .analytics-pulse-strip { margin: 0; }
.dash-row { display: grid; gap: 1rem; align-items: stretch; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }
.dash-row:not(:has(> .card)) { display: none; }
/* The cards keep their .mb-3 elsewhere, which Bootstrap marks !important; inside a row the gap replaces it. */
.dash-row > .card { min-width: 0; margin-bottom: 0 !important; }
/* KPI figures inside a card: a grid on the card's width instead of viewport columns. */
.dash-kpis { display: grid; grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr)); gap: .9rem 1rem; margin-bottom: 1.1rem; }
.dash-kpis .kpi-figure { display: block; font-size: 1.375rem; overflow-wrap: anywhere; }
.dash-kpis .lbl { display: flex; align-items: center; gap: .25rem; margin-top: .2rem; }
/* The age ladder in a card: room for a seven-digit count and a calmer rhythm. */
.analytics-dashboard .age-ladder { gap: .85rem; margin-top: .25rem; }
.analytics-dashboard .age-row { grid-template-columns: 6.75rem minmax(0, 1fr) 4.5rem; }
.analytics-dashboard .age-lbl { white-space: nowrap; }
.analytics-dashboard .age-bar { height: 10px; border-radius: 5px; }
/* Tables inside a card: the card body already pads, so the outer cells drop theirs. */
.analytics-dashboard .card-body .table-airy > :not(caption) > * > * { padding-left: .5rem; padding-right: .5rem; }
.analytics-dashboard .card-body .table-airy > :not(caption) > * > :first-child { padding-left: 0; }
.analytics-dashboard .card-body .table-airy > :not(caption) > * > :last-child { padding-right: 0; }
/* Three across needs about 336px per card (age ladder 6.75rem + bar + 4.5rem, two KPI columns of 8.5rem), which is
   1040px of shell. A 1440px window with the sidebar open leaves 1071px, so the break sits just under that. */
@container analytics (max-width: 1039.98px) {
    .dash-row { grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .dash-row > .card:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
@container analytics (max-width: 719.98px) {
    .dash-row { grid-template-columns: minmax(0, 1fr); }
    .dash-row > .card:last-child:nth-child(odd) { grid-column: auto; }
}

/* ===== Aviso de función en beta (fragments/notice/beta.html) =====
   Informativo, no una alarma: superficie blanca, borde de marca a la izquierda y el acento interactivo en el ícono. */
.beta-notice {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 1rem;
    padding: 0.5rem 0.75rem;
    background: var(--brand-white);
    border: 1px solid var(--brand-line);
    border-left: 3px solid var(--brand-blue-deep);
    border-radius: var(--border-radius);
    color: var(--brand-body);
    font-size: 0.875rem;
}
.beta-notice > .ph-duotone { color: var(--brand-blue-deep); font-size: 1.125rem; }
