/* Special Services — volume dashboard (shipments / pieces / weight).
 *
 * Same native stack as the sibling dashboards (newacct, shipment_insights):
 * plain HTML/CSS/JS, no framework, no build step. Views carry their own inline
 * styles; this file holds global resets, fonts, keyframes, and the few things
 * inline styles can't express (::selection, the animation gate).
 *
 * Animation gate: app.js re-renders #app via innerHTML on every state change,
 * so entrance animations must be GATED or they replay on every click. app.js
 * stamps #app with `data-anim` tokens for the render it just produced:
 *     view  -> the page was switched      (container reveal)
 *     data  -> the shown data changed
 *     modal -> a drill modal was opened   (pop, once)
 *     none  -> minor tick (theme, sort)   -> no entrance
 */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body { font-family: 'IBM Plex Sans', system-ui, sans-serif; }

::selection { background: #0ea5b726; }

select { font-family: 'IBM Plex Sans', system-ui, sans-serif; }

/* Reproduce the prototype's hover affordance with real CSS. */
[data-hover]:hover { background: var(--hover-bg, transparent) !important; }

/* ---- entrance gate ------------------------------------------------------ */

/* Reveal the view container when the page is switched. */
#app[data-anim~="view"] > div { animation: viewIn .24s ease both; }
@keyframes viewIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Staggered list reveal — each row fades + slides up (per-rank delay set
   inline, rank 1 first); the bar inside grows from the left a beat later. */
#app[data-anim~="data"] .li-row { animation: rowIn .42s ease both; }
@keyframes rowIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.li-bar { transform-box: fill-box; transform-origin: left; }
#app[data-anim~="data"] .li-bar { animation: barGrow .5s ease both; }
@keyframes barGrow {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* KPI + card fade-in on data/view renders. */
#app[data-anim~="data"] .fade-in { animation: fadeIn .45s ease both; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

/* Modal (drill-down) pop. Gated on the "modal" token so it plays only on the
 * render that OPENS the modal — not on in-modal sort/search/page clicks. */
#app[data-anim~="modal"] .modal-card { animation: modalIn .18s ease both; }
@keyframes modalIn {
  from { opacity: 0; transform: translateY(8px) scale(.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
#app[data-anim~="modal"] .modal-back { animation: fadeIn .16s ease both; }

/* Globe host: fills its card; three.js canvas sits inside. */
.globe-host { width: 100%; height: 520px; border-radius: 12px; overflow: hidden; position: relative; }
.globe-host canvas { display: block; }

/* Accessibility: honour the OS "reduce motion" setting. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
