/* ── SpoolsManager.razor ── */
/*
 * :root — --card-bg/--text-primary/--text-secondary/--kpi-success/--kpi-warning/--kpi-danger/--kpi-info
 * eliminados: idénticos al patrón canónico (css/components/kpiDashboard.css). --kpi-primary y --body-bg
 * (que aquí valen slate-700/slate-100, distintos del azul/gray-100 canónico) se mantienen INLINE en
 * SpoolsManager.razor — moverlos aquí filtraría el tema slate/zone de Spools a toda la app, ya que
 * :root no tiene forma de quedar scopeado a una sola página. Ver residual en el propio .razor.
 */
:root {
    /* Zone Colors */
    --zone-workshop:  var(--atlas-indigo-500); /* Indigo-500 */
    --zone-laydown:   var(--atlas-amber-500); /* Amber-500 */
    --zone-site:      #DCFF5C; /* Lime/Special */
    --zone-banner:    #334155; /* Slate-700 */

    /* Status Colors */
    --status-transit:   var(--atlas-orange-800); /* Brownish Orange (Orange-800) */
    --status-confirmed: var(--atlas-emerald-500); /* Emerald-500 original */
    /* Cyan, not yellow: "pending arrival" sat ΔE 10.4 from the Laydown amber under deuteranopia,
       so the two stages were indistinguishable wherever they met (the delivery ring, the chips). */
    --status-pending:   #0891b2; /* Cyan-600 */
    --status-priority:  var(--atlas-red-500); /* Red-500 */
    --status-info:     var(--atlas-blue-500); /* Blue-500 */

    /* Alias usado por --body-bg residual en SpoolsManager.razor / SpoolsLogs.razor */
    --spools-bg: var(--atlas-slate-100); /* Slate-100 */
}

/* .asset-dashboard / .dashboard-card / .kpi-stat-card(+::after) / .kpi-icon-wrapper / .kpi-content /
   .kpi-value / .kpi-label / .banner-title / .banner-subtitle / .kpi-clickable(+:hover) /
   .accent-success/.accent-warning/.accent-info/.accent-danger eliminados: idénticos (o con drift de
   padding/position/overflow/margin/opacity) al patrón canónico de css/components/kpiDashboard.css —
   el canónico gana. .dashboard-banner y .accent-primary se mantienen INLINE en el .razor (ver arriba). */

/* Único bit funcional de .kpi-clickable que no existe en el canónico (que usa .kpi-active-filter, no
   .kpi-active): se conserva como selector propio, sin colisión. */
.kpi-clickable.kpi-active { box-shadow: 0 0 0 2px var(--accent-color) inset; }

/* Confirmed / Pending share one value line; colour tells them apart, the label names them. */
.kpi-confirmed { color: var(--status-confirmed); }
.kpi-pending   { color: var(--status-pending); }
.kpi-sep { color: var(--text-secondary); font-weight: 400; margin: 0 .3rem; }

/* ── Delivery Progress panel ── */
/* The donut is the panel's headline: the confirmed % is drawn in its hole by ECharts, so there is
   no separate hero block. Height is fixed because the canvas needs a laid-out box to size against,
   and it includes the outside labels' band so they are never clipped by the card. */
.cp-chart { background: var(--atlas-gray-50); border: 1px solid rgba(0,0,0,.06); border-radius: 10px; padding: 1.25rem; height: 100%; }
.cp-chart-title { font-size: .8rem; font-weight: 600; color: var(--text-secondary); margin-bottom: .75rem; }
.cp-matrix { width: 100%; height: 300px; }
.cp-chart-hint { font-size: .72rem; color: var(--text-secondary); margin-top: .5rem; }
.cp-subtitle { font-size: .75rem; font-weight: 500; color: var(--text-secondary); margin-left: .4rem; }

/* The panel's headline number: share of scanned spools that sit where the DB says. */
.cp-hero { background: var(--atlas-gray-50); border: 1px solid rgba(0,0,0,.06); border-radius: 10px; padding: 1.1rem 1.25rem; }
.cp-hero-value { font-size: 2.5rem; font-weight: 700; color: var(--text-primary); line-height: 1; }
.cp-hero-unit  { font-size: 1.25rem; font-weight: 600; color: var(--text-secondary); margin-left: .15rem; }
.cp-hero-label { font-size: .82rem; color: var(--text-secondary); margin-top: .3rem; }
.cp-hero-foot  { font-size: .78rem; color: var(--text-secondary); margin-top: .5rem; }

/* A deviation drill-down is a problem, and reads as one. */
.cp-drill.is-deviation { border-color: rgba(239, 68, 68, .35); }
.cp-drill-badge {
    font-size: .6rem; font-weight: 700; letter-spacing: .02em;
    color: var(--atlas-red-700); background: rgba(239, 68, 68, .12);
    border-radius: 4px; padding: .1rem .35rem; margin-left: .4rem; vertical-align: middle;
}

.cp-tiles { display: flex; gap: .6rem; }
.cp-tile { display: flex; align-items: center; gap: .75rem; background: var(--atlas-white); border: 1px solid rgba(0,0,0,.06); border-radius: 8px; padding: .6rem .75rem; flex: 1; }
.cp-tile-icon { font-size: 1.25rem; flex-shrink: 0; }
.cp-tile-icon.is-muted   { color: var(--text-secondary); }
.cp-tile-icon.is-danger  { color: var(--status-priority); }
.cp-tile-icon.is-transit { color: var(--status-transit); }
.cp-tile-value { font-size: 1.1rem; font-weight: 700; color: var(--text-primary); line-height: 1.1; }
.cp-tile-label { font-size: .72rem; color: var(--text-secondary); }

/* Table view: the exact numbers, and the slices' keyboard-reachable twin. */
.cp-table { font-size: .78rem; background: var(--atlas-white); border-radius: 8px; overflow: hidden; }
.cp-table thead th { font-size: .7rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-secondary); }
.cp-row { cursor: pointer; transition: background .12s; }
.cp-row:hover { background: rgba(0,0,0,.03); }
.cp-row:focus-visible { outline: 2px solid var(--status-info); outline-offset: -2px; }
.cp-row.is-selected { background: rgba(59, 130, 246, .08); box-shadow: inset 3px 0 0 var(--status-info); }

/* Drill-down: the spools behind the selected slice. */
.cp-drill { border: 1px solid rgba(0,0,0,.06); border-radius: 10px; overflow: hidden; }
.cp-drill-head { display: flex; align-items: center; gap: .6rem; padding: .75rem 1rem; background: var(--atlas-gray-50); border-bottom: 1px solid rgba(0,0,0,.06); }
.cp-drill-title { font-size: .9rem; font-weight: 700; color: var(--text-primary); }
.cp-drill-count { font-size: .75rem; color: var(--text-secondary); }
.cp-drill-search { max-width: 320px; margin-left: auto; }
/* Sub-area chips inside the drill-down; they reuse the Material Tracking chip styling. */
.cp-drill-subareas {
    display: flex; flex-wrap: wrap; gap: .3rem;
    padding: .6rem 1rem; background: var(--atlas-white);
    border-bottom: 1px solid rgba(0,0,0,.06);
}
/* No max-height / inner scroll any more: the table holds one page of 10 rows, so it never needs a
   nested scrollbar of its own. */
.cp-drill-table thead th { font-size: .7rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-secondary); }
.cp-drill-pager { border-top: 1px solid rgba(0,0,0,.06); }
.cp-drill-desc { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Accent indigo especifico de Spools (Workshop) — el canónico usa azul genérico via --kpi-primary;
   se mantiene INLINE en SpoolsManager.razor (ver residual) por ser un valor distinto y colisionar
   con .accent-primary del patrón canónico. */
.accent-transit { --accent-color: var(--status-transit);  --accent-bg: rgba(154, 52, 18, 0.1); }
.accent-confirmed { --accent-color: var(--status-confirmed); --accent-bg: rgba(16, 185, 129, 0.1); }
.accent-dark    { --accent-color: var(--zone-banner);   --accent-bg: rgba(51, 65, 85, 0.1); }
.accent-site    { --accent-color: #6d8a00;               --accent-bg: rgba(220, 255, 92, 0.3); }
/* PCA status: amber, to read as a warning distinct from the red Incidents tab. */
.accent-pca     { --accent-color: var(--zone-laydown);   --accent-bg: rgba(245, 158, 11, 0.1); }

.accent-transit .atlas-table-title { color: var(--status-transit); }
.accent-transit .atlas-table-title i { color: var(--status-transit); }

/* Banner top-right tabs (el propio .dashboard-banner se mantiene inline en el .razor — ver arriba) */
/* Sync row pinned to the banner's top-right corner; the zone tabs keep the bottom-right,
   so neither reflows the other as the timestamp appears and disappears. The tabs reserve
   the sync row's height above them, otherwise the two would overlap on a short banner. */
/* 2rem is the button's height: the least that keeps the tabs clear of the pinned Update. */
.banner-right { display: flex; align-items: flex-end; padding-top: 2rem; }
.banner-sync {
    /* top matches the banner's padding-top so the Update button sits on the title's line. */
    position: absolute; top: 1.25rem; right: 1.5rem;
    display: flex; align-items: center; gap: .75rem;
}

/* No room to pin it on narrow screens: let it flow above the tabs instead of covering the title. */
@media (max-width: 768px) {
    .banner-sync { position: static; flex-wrap: wrap; justify-content: flex-end; width: 100%; }
    .banner-right { padding-top: 0; }
}

.zone-tabs { border-bottom: none; }
.zone-tabs .nav-link {
    border: none; border-bottom: 2px solid transparent;
    background: none; color: var(--text-secondary);
    padding: .625rem 1rem; font-weight: 500;
}
.zone-tabs .nav-link.active {
    color: var(--kpi-primary); border-bottom-color: var(--kpi-primary); background: transparent;
}
.zone-tabs .nav-link:hover:not(.active) { color: var(--text-primary); }

/* Flow diagram */
.flow-card {
    background: var(--atlas-white); border: 1px solid rgba(0,0,0,.05);
    border-radius: 12px; box-shadow: var(--atlas-shadow-1);
    padding: 1.25rem 1.5rem;
}
.flow-title { font-size: 1rem; font-weight: 700; color: var(--text-primary); }
.mt-last-loaded { font-size: .75rem; color: var(--text-secondary); white-space: nowrap; }
.mt-load-duration { font-weight: 600; color: var(--text-primary); }
.flow-diagram {
    display: flex; align-items: stretch; gap: .5rem;
    overflow-x: auto;
}
.flow-node {
    flex: 1 1 0;
    min-width: 180px;
    border: 1px solid rgba(0,0,0,.06);
    border-top: 3px solid var(--accent-color);
    border-radius: 10px;
    padding: .9rem 1rem;
    background: var(--atlas-gray-50);
    display: flex; align-items: center; gap: .75rem;
}
.flow-node-icon {
    width: 40px; height: 40px; border-radius: 10px;
    background: var(--accent-bg); color: var(--accent-color);
    display: flex; align-items: center; justify-content: center; font-size: 1.2rem;
    flex-shrink: 0;
}
.flow-node-title { font-weight: 700; color: var(--text-primary); }
.flow-node-stats { display: flex; gap: .75rem; font-size: .8rem; color: var(--text-secondary); margin-top: 2px; }
.flow-node-stats .stat-confirmed i { color: var(--kpi-success); }
.flow-node-stats .stat-pending   i { color: var(--kpi-warning); }
.flow-arrow {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    color: var(--text-secondary); flex-shrink: 0; padding: 0 .25rem;
}
.flow-arrow i { font-size: 1.5rem; color: var(--kpi-info); }
.flow-arrow-label { font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; }

/* Material Tracking: three columns (one per zone) + clickable in-transit connectors */
/* stretch, not start: every column is as tall as the tallest, so Workshop / Laydown / Site always
   line up regardless of how many spools each holds or whether it has a sub-area chip row. */
.mt-columns {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: .75rem;
    align-items: stretch;
}
@media (max-width: 992px) { .mt-columns { grid-template-columns: 1fr; } }
.mt-zone {
    display: flex; flex-direction: column;
    border: 1px solid rgba(0,0,0,.06);
    border-top: 4px solid var(--accent-color);
    border-radius: 10px;
    background: var(--atlas-gray-50);
    overflow: hidden;
}
.mt-zone-head {
    display: flex; align-items: center; gap: .75rem;
    padding: .75rem 1rem;
    background: var(--atlas-white);
    border-bottom: 1px solid rgba(0,0,0,.05);
}
.mt-zone-count {
    margin-left: auto; font-size: .75rem; font-weight: 600;
    color: var(--text-secondary);
    background: var(--accent-bg); color: var(--accent-color);
    padding: .15rem .6rem; border-radius: 999px;
}
.mt-zone-spools {
    /* Reserve a full page's worth of rows so a column holding 2 spools is as tall as one holding 10.
       Derived from the page size rather than a hand-tuned total, so it stays right if either changes.
       Chips are locked to one line (see .mt-spool-chip), which is what makes the row height a constant. */
    --mt-page-size: 10;
    --mt-chip-h: 1.87rem;
    --mt-chip-gap: .4rem;

    display: flex; flex-direction: column; gap: var(--mt-chip-gap);
    padding: .75rem 1rem; flex: 1;
    min-height: calc(
        var(--mt-page-size) * (var(--mt-chip-h) + 2px)
        + (var(--mt-page-size) - 1) * var(--mt-chip-gap)
    );
}
.mt-spool-chip {
    display: flex; align-items: center; gap: .4rem;
    width: 100%;
    padding: .35rem .7rem;
    background: var(--atlas-white);
    border: 1px solid rgba(0,0,0,.08);
    border-radius: 8px;
    font-size: .78rem;
    /* One line, always. A chip that wraps is taller than the row height the column reserves, which
       would put the three columns out of step again — so the description is what gets clipped. */
    height: calc(var(--mt-chip-h) + 2px);
    white-space: nowrap;
    overflow: hidden;
    flex-shrink: 0;
}
.mt-spool-chip { cursor: pointer; transition: border-color .15s, box-shadow .15s; }
.mt-spool-chip:hover { border-color: var(--kpi-info); box-shadow: 0 1px 4px rgba(0,0,0,.08); }
.mt-spool-chip:hover .mt-spool-qr { opacity: 1; }
/* The spool's name never gets clipped; the description absorbs whatever room is left. */
.mt-spool-id { font-weight: 700; color: var(--text-primary); flex-shrink: 0; }
.mt-spool-confirmed { color: var(--status-confirmed); font-size: .95rem; flex-shrink: 0; }
.mt-spool-qr { color: var(--text-secondary); font-size: .9rem; opacity: .35; flex-shrink: 0; }
/* Scan location disagrees with the registered position (an incident exists for it). */
.mt-spool-pca { color: var(--status-priority); font-size: .95rem; flex-shrink: 0; }
/* visibility, not display: the slot has to keep its width on rows with no mismatch, or the sub-area
   tag and QR icon beside it would shift and the column would stop lining up. */
.mt-spool-pca.is-empty { visibility: hidden; }
.mt-spool-transit { color: var(--status-transit); font-size: .9rem; }

/* Laydown sub-area (GCP yard) chips, under that column's search box. */
/* Same 1rem horizontal inset as the search box above it.
   min-height is the height of one chip row: zones without sub-areas render this element empty, so
   Workshop / Laydown / Site stay the same height and their spool lists start at the same line. */
.mt-subarea-filter {
    display: flex; flex-wrap: wrap; gap: .3rem;
    margin: .5rem 1rem .25rem;
    min-height: 1.45rem;
}
.mt-subarea-chip {
    display: inline-flex; align-items: center; gap: .3rem;
    border: 1px solid rgba(0,0,0,.12); background: var(--atlas-white); color: var(--text-secondary);
    border-radius: 999px; padding: .15rem .5rem; font-size: .7rem; font-weight: 600;
    cursor: pointer; transition: background .12s, border-color .12s, color .12s;
}
.mt-subarea-chip:hover { background: rgba(0,0,0,.03); }
/* The active chip takes its column's zone colour (Workshop blue, Laydown amber, Site green) rather
   than a single hardwired amber, so the filter reads as belonging to its zone. Falls back to the
   Laydown amber anywhere the accent isn't set (e.g. the PCA drill outside a zone column). */
.mt-subarea-chip.is-active {
    background: var(--accent-color, var(--zone-laydown));
    border-color: var(--accent-color, var(--zone-laydown));
    color: var(--atlas-white);
}
.mt-subarea-n { font-weight: 700; opacity: .85; }
.mt-subarea-chip.is-active .mt-subarea-n { opacity: 1; }

/* A yard chip and its expand arrow ride together as one unit and wrap together. */
.mt-subarea-group { display: inline-flex; align-items: stretch; gap: 2px; }
.mt-subarea-expand {
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid rgba(0,0,0,.12); background: var(--atlas-white); color: var(--text-secondary);
    border-radius: 999px; padding: 0 .35rem; font-size: .72rem; line-height: 1;
    cursor: pointer; transition: background .12s, border-color .12s, color .12s;
}
.mt-subarea-expand:hover { background: rgba(0,0,0,.03); }
.mt-subarea-expand.is-open {
    background: var(--accent-bg, rgba(245,158,11,.15));
    border-color: var(--accent-color, var(--zone-laydown));
    color: var(--accent-color, var(--zone-laydown));
}
/* Level-2 sub-sections of the expanded yard: a second row, indented under the yard row so the
   hierarchy reads without disturbing the order of the yard chips above. */
.mt-subarea-children {
    display: flex; flex-wrap: wrap; gap: .3rem;
    margin: 0 1rem .3rem 1.4rem;
}
.mt-subarea-chip.is-child { font-size: .66rem; padding: .12rem .45rem; }

/* The yard a Laydown spool sits in, on its chip. */
/* Fixed-width slot: every yard tag occupies the same box whatever its name, so the tags and the
   QR icons after them line up down the column instead of drifting with the spool's name length. */
.mt-subarea-tag {
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    width: 3.4rem;
    font-size: .55rem; font-weight: 700; letter-spacing: .02em;
    /* Tinted in the column's zone colour (see .mt-subarea-chip.is-active), amber fallback. */
    color: var(--accent-color, var(--atlas-amber-800)); background: var(--accent-bg, rgba(245, 158, 11, .15));
    border-radius: 4px; padding: .1rem .2rem;
    white-space: nowrap; overflow: hidden;
}
/* No yard: keep the slot (so the QR icons stay aligned) but let it recede. */
.mt-subarea-tag.is-empty { color: var(--text-secondary); background: rgba(0,0,0,.04); }
.mt-spool-desc { flex: 1; min-width: 0; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mt-empty { color: var(--text-secondary); font-size: .8rem; font-style: italic; }

/* Admin "create random spool" button inside the Workshop column. */
.mt-zone-addspool { display: block; width: calc(100% - 2rem); margin: .75rem 1rem 0; }

/* Search box (one per zone column) */
.mt-search { position: relative; margin: .75rem 1rem .25rem; }
.mt-search-icon { position: absolute; left: .6rem; top: 50%; transform: translateY(-50%); color: var(--text-secondary); font-size: .9rem; pointer-events: none; }
/* Descendant selector (0,2,0) so these beat Bootstrap's .form-control-sm shorthand
   `padding: .25rem .5rem`, which otherwise resets padding-left and slides the text
   under the magnifying-glass icon. */
.mt-search .mt-search-input { padding-left: 2rem; padding-right: 2rem; }
.mt-search-clear {
    position: absolute; right: .4rem; top: 50%; transform: translateY(-50%);
    border: none; background: transparent; color: var(--text-secondary);
    cursor: pointer; line-height: 1; padding: .15rem;
}
.mt-search-clear:hover { color: var(--status-priority); }

/* Per-zone pager */
.mt-pager {
    display: flex; align-items: center; justify-content: center; gap: 1rem;
    padding: .5rem 1rem .75rem; border-top: 1px solid rgba(0,0,0,.05);
}
.mt-pager-info { font-size: .78rem; font-weight: 600; color: var(--text-secondary); }

/* In-transit connectors between zones (one row, side by side) */
.mt-connectors { display: flex; gap: .75rem; margin-top: .75rem; }
@media (max-width: 992px) { .mt-connectors { flex-direction: column; } }
.mt-connector { flex: 1; min-width: 0; }
.mt-transit-btn {
    display: flex; align-items: center; gap: .5rem; width: 100%;
    border: 1px dashed var(--kpi-info); background: rgba(59, 130, 246, 0.06);
    color: var(--text-primary); border-radius: 8px;
    padding: .4rem .75rem; font-size: .8rem; font-weight: 600;
    cursor: pointer; transition: background .15s ease;
}
.mt-transit-btn:hover { background: rgba(59, 130, 246, 0.12); }
.mt-transit-btn.open { background: rgba(59, 130, 246, 0.12); }
/* A leg actually carrying spools: solid transit-coloured border + truck so the flow pops. */
.mt-transit-btn.active {
    border-style: solid; border-color: var(--status-transit);
    background: rgba(154, 52, 18, 0.08); color: var(--status-transit);
}
.mt-transit-btn.active i:first-child { color: var(--status-transit); }
.mt-transit-btn i:first-child { color: var(--kpi-info); font-size: 1rem; }
.mt-transit-label { text-transform: uppercase; letter-spacing: .03em; font-size: .72rem; }
.mt-transit-panel {
    margin-top: .4rem; border: 1px solid rgba(0,0,0,.08); border-radius: 8px;
    background: var(--atlas-white); overflow: hidden;
}
.mt-transit-row {
    display: grid; grid-template-columns: 1fr auto auto auto; gap: 1rem; align-items: center;
    padding: .5rem .75rem; border-bottom: 1px solid rgba(0,0,0,.05);
}
.mt-transit-row:last-child { border-bottom: none; }
.mt-transit-row:hover { background: var(--atlas-slate-50); }

/* Zone info card */
.zone-info-card {
    background: var(--atlas-white); border: 1px solid rgba(0,0,0,.05);
    border-radius: 12px; box-shadow: var(--atlas-shadow-1);
    padding: 1rem 1.25rem; display: flex; align-items: center; gap: 1rem;
}
.zone-info-icon {
    width: 44px; height: 44px; border-radius: 10px;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.3rem; background: var(--accent-bg); color: var(--accent-color); flex-shrink: 0;
}

/* Table */
.spools-table thead th {
    font-size: .75rem; text-transform: uppercase; letter-spacing: .04em;
    color: var(--text-secondary); font-weight: 700;
    border-bottom: 1px solid rgba(0,0,0,.08); padding: .75rem .5rem;
}
.spools-table tbody td { padding: .75rem .5rem; vertical-align: middle; border-bottom: 1px solid rgba(0,0,0,.05); }
.spools-table tbody tr.row-in-transit td { background: rgba(245, 158, 11, .05); }

/* Pills */
.pill {
    display: inline-block; padding: 2px 10px; border-radius: 999px;
    font-size: .72rem; font-weight: 600; letter-spacing: .02em; border: 1px solid transparent;
}
.pill-priority-high   { background: rgba(239, 68, 68, .1);  color: var(--atlas-red-800); border-color: rgba(239, 68, 68, .25); }
.pill-priority-medium { background: rgba(245, 158, 11, .1); color: var(--atlas-amber-800); border-color: rgba(245, 158, 11, .25); }
.pill-priority-low    { background: rgba(16, 185, 129, .1); color: var(--atlas-emerald-800); border-color: rgba(16, 185, 129, .25); }
.pill-status          { background: rgba(37, 99, 235, 0.08); color: var(--atlas-blue-800); border-color: rgba(37, 99, 235, 0.2); }
.pill-received-site   { background: rgba(16, 185, 129, 0.12); color: var(--atlas-emerald-800); border-color: rgba(16, 185, 129, 0.3); }
.pill-in-transit      { background: rgba(154, 52, 18, 0.12); color: var(--status-transit); border-color: rgba(154, 52, 18, 0.3); }

.anim-shake { animation: shake 0.4s cubic-bezier(.36,.07,.19,.97) both; }
@keyframes shake {
  10%, 90% { transform: translate3d(-1px, 0, 0); }
  20%, 80% { transform: translate3d(2px, 0, 0); }
  30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }
  40%, 60% { transform: translate3d(4px, 0, 0); }
}

/* Incidents: severity pills + the always-red Incidents tab.
   CRITICAL is twice as long as LOW, so pin a common width and centre the label —
   otherwise the pill outlines stagger down the column. */
.pill-severity, .pill-incident { min-width: 6.5rem; text-align: center; }
.pill-incident { min-width: 8rem; }

.pill-severity-critical { background: rgba(127, 29, 29, .12); color: #7f1d1d; border-color: rgba(127, 29, 29, .3); }
.pill-severity-high     { background: rgba(239, 68, 68, .1);  color: var(--atlas-red-800); border-color: rgba(239, 68, 68, .25); }
.pill-severity-medium   { background: rgba(245, 158, 11, .1); color: var(--atlas-amber-800); border-color: rgba(245, 158, 11, .25); }
.pill-severity-low      { background: rgba(16, 185, 129, .1); color: var(--atlas-emerald-800); border-color: rgba(16, 185, 129, .25); }

/* Incident lifecycle: two open states (red / amber) and three closed ones (green / grey). */
.pill-incident-open         { background: rgba(239, 68, 68, .1);  color: var(--atlas-red-800); border-color: rgba(239, 68, 68, .25); }
.pill-incident-acknowledged { background: rgba(245, 158, 11, .1); color: var(--atlas-amber-800); border-color: rgba(245, 158, 11, .25); }
.pill-incident-resolved     { background: rgba(16, 185, 129, .1); color: var(--atlas-emerald-800); border-color: rgba(16, 185, 129, .25); }
.pill-incident-dismissed    { background: rgba(107, 114, 128, .1); color: var(--atlas-gray-700); border-color: rgba(107, 114, 128, .25); }
.pill-incident-closed       { background: rgba(107, 114, 128, .1); color: var(--atlas-gray-700); border-color: rgba(107, 114, 128, .25); }

.zone-tabs .nav-link.nav-link-incidents { color: var(--status-priority); font-weight: 600; }
.zone-tabs .nav-link.nav-link-incidents:hover:not(.active) { color: var(--atlas-red-800); }
.zone-tabs .nav-link.nav-link-incidents.active { color: var(--status-priority); border-bottom-color: var(--status-priority); }

/* PCA status tab: amber, matching its warning accent and set apart from the red Incidents tab. */
.zone-tabs .nav-link.nav-link-pca { color: var(--zone-laydown); font-weight: 600; }
.zone-tabs .nav-link.nav-link-pca:hover:not(.active) { color: var(--atlas-amber-700); }
.zone-tabs .nav-link.nav-link-pca.active { color: var(--zone-laydown); border-bottom-color: var(--zone-laydown); }

.spools-table tbody tr.row-incident-open td { background: rgba(239, 68, 68, .04); }

.pill-zone-workshop { background: rgba(99, 102, 241, 0.1); color: #4338ca; border-color: rgba(99, 102, 241, 0.2); }
.pill-zone-laydown  { background: rgba(245, 158, 11, 0.1); color: var(--atlas-amber-700); border-color: rgba(245, 158, 11, 0.2); }
.pill-zone-site     { background: rgba(220, 255, 92, 0.2); color: #4d6100; border-color: rgba(220, 255, 92, 0.4); }

/* QR modal extras */
.qr-wrapper {
    display: inline-block; padding: 12px; background: var(--atlas-white);
    border: 1px solid rgba(0,0,0,.08); border-radius: 12px;
    box-shadow: 0 2px 10px rgba(0,0,0,.06);
}
.qr-wrapper img { display: block; }
.qr-meta { background: var(--atlas-gray-50); border-radius: 10px; padding: .75rem 1rem; border: 1px solid rgba(0,0,0,.05); }
.packing-list-ul { padding-left: 1.25rem; font-size: .9rem; }
.packing-list-ul li { margin-bottom: 2px; }

/* Packing List Detail Modal */
.pl-info-card {
    background: var(--atlas-white); border: 1px solid rgba(0,0,0,.06); border-radius: 10px;
    padding: .75rem 1rem; display: flex; flex-direction: column; height: 100%;
}
.pl-info-label { font-size: .7rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-secondary); font-weight: 600; margin-bottom: 2px; }
.pl-info-value { font-size: .95rem; font-weight: 500; color: var(--text-primary); }
.pl-clickable { cursor: pointer; transition: color 0.15s; }
.pl-clickable:hover { color: #4338ca !important; text-decoration: underline; }

/* ── SpoolsLogs.razor ── */
/*
 * :root — --kpi-success/--kpi-warning/--kpi-danger/--card-bg/--text-primary/--text-secondary
 * eliminados: idénticos al canónico. --status-transit eliminado: idéntico al ya definido arriba en
 * la sección de SpoolsManager.razor (mismo valor, mismo :root global). --kpi-primary y --body-bg
 * (distintos del canónico) se mantienen INLINE en SpoolsLogs.razor.
 * .asset-dashboard eliminado (drift de min-height:100vh vs 100% canónico, sin justificación de
 * comentario — se asume no intencional, gana el canónico).
 * .dashboard-card/.kpi-stat-card(+::after)/.kpi-icon-wrapper/.kpi-content/.kpi-value/.kpi-label
 * eliminados: drift de padding/tamaño frente al canónico — gana el canónico.
 * .dashboard-banner eliminado: solo difiere en margin-bottom (2rem vs 1rem canónico); el color de
 * border-left ya se resuelve via el --kpi-primary residual inline.
 * .banner-title se mantiene INLINE (usa var(--kpi-primary) en vez de var(--text-primary): color
 * distinto y funcional, no drift).
 * .banner-subtitle eliminado: idéntico al canónico.
 * .accent-success/.accent-warning/.accent-danger/.accent-info eliminados: idénticos al canónico.
 * .accent-transit eliminado: idéntico al ya movido arriba en la sección de SpoolsManager.razor.
 * .kpi-clickable/.kpi-active-filter eliminados: .kpi-active-filter es funcionalmente idéntico al
 * canónico (mismo box-shadow, solo cambia el orden de "inset" en el shorthand); el resto es drift
 * de transición/hover — gana el canónico.
 * #registros-tabs eliminado: BYTE-IDÉNTICO (salvo la regla .badge, puramente aditiva) al ya
 * globalizado en css/pages/accessControl/accessControl-pages.css — no se recopia.
 */
.accent-dark    { --accent-color: var(--kpi-primary); --accent-bg: rgba(51, 65, 85, 0.1); }
.accent-indigo  { --accent-color: var(--atlas-indigo-500); --accent-bg: rgba(99, 102, 241, 0.1); }

.search-box { position: relative; }
.search-box i { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--text-secondary); font-size: .9rem; }

.user-avatar { width: 28px; height: 28px; border-radius: 50%; background: var(--atlas-gray-100); border: 1px solid rgba(0,0,0,.05); display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; color: var(--text-secondary); }

/* Keep every cell on a single line; the .table-responsive wrapper scrolls sideways
   instead of letting Details or Location wrap and shove the rows out of alignment. */
.spool-logs-table th,
.spool-logs-table td { white-space: nowrap; }

/* .pill idéntico (salvo letter-spacing, añadido de forma inocua) al ya movido arriba en la sección
   de SpoolsManager.razor — no se recopia. */

/* Pills are inline-block, so each one is as wide as its label: ARRIVAL CONFIRMED vs BROKEN
   staggered the outlines down the column. Pin a common width per column and centre the text.
   min-width (not width) so a longer label grows rather than being clipped. */
.pill-uniform { text-align: center; }
.pill-uniform-action { min-width: 9.5rem; }
.pill-uniform-status { min-width: 8.5rem; }
/* .pill-status idéntico ya movido (SpoolsManager). .pill-zone-workshop / .pill-zone-laydown idénticos
   ya movidos (SpoolsManager) — no se recopian. .pill-zone-site DIFIERE (verde esmeralda aquí vs
   verde lima en SpoolsManager) y .pill-in-transit DIFIERE (opacidades distintas) — ambas se
   mantienen INLINE en SpoolsLogs.razor. .pill-priority-high idéntico (subset) ya movido — no se
   recopia. */
.pill-zone-erected  { background: rgba(99, 102, 241, 0.12); color: var(--atlas-indigo-800); border-color: rgba(99, 102, 241, 0.25); }

/* Loading skeleton: a shimmering placeholder row per cell, shown while a table's data is still
   in flight so the tables never flash their "nothing found" empty state during a load. */
.skeleton-bar {
    display: block; height: .75rem; width: 100%; border-radius: 4px;
    background: linear-gradient(90deg, #eef2f7 25%, var(--atlas-slate-200) 37%, #eef2f7 63%);
    background-size: 400% 100%;
    animation: skeleton-shimmer 1.4s ease-in-out infinite;
}
@keyframes skeleton-shimmer {
    0%   { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

/* .mt-pager DIFIERE del ya movido (SpoolsManager: padding .5rem 1rem .75rem, sin background) —
   se mantiene INLINE en SpoolsLogs.razor. .mt-pager-info idéntico — no se recopia. */

.cursor-pointer { cursor: pointer; }
.sort-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 4px;
    background: transparent;
    transition: all .15s ease;
    color: var(--text-secondary);
}
.cursor-pointer:hover .sort-btn { background: rgba(0,0,0,0.05); color: var(--kpi-primary); }
.sort-btn.active { color: var(--kpi-primary) !important; background: rgba(51, 65, 85, 0.08) !important; }
.sort-indicator { font-size: 0.85rem; }

/* ── TerminalStatusSms.razor ── */
/*
 * :root completo eliminado: todos sus valores (--kpi-primary var(--atlas-blue-600), --kpi-success/warning/danger,
 * --card-bg, --body-bg var(--atlas-gray-100), --text-primary, --text-secondary) son BYTE-IDÉNTICOS al canónico
 * (css/components/kpiDashboard.css) — esta página no tiene tema propio, usa el azul/gray estándar.
 * .asset-dashboard/.dashboard-card/.kpi-stat-card(+::after)/.kpi-icon-wrapper/.kpi-content/
 * .kpi-value/.kpi-label/.dashboard-banner/.banner-title/.banner-subtitle/.accent-success/
 * .accent-warning/.accent-danger eliminados: idénticos o drift trivial frente al canónico.
 * .accent-info se mantiene INLINE en TerminalStatusSms.razor: usa índigo var(--atlas-indigo-500) en vez del azul
 * var(--atlas-blue-500) canónico — sin comentario que lo justifique, se preserva por precaución (ver informe).
 */
