/* New Account Insights — June 2026 new-accounts dashboard.
 *
 * Same native stack as the sibling dashboards (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
 *     none  -> minor tick (theme, drill open) -> no entrance
 */

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

::selection { background: #4f2fff26; }

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; } }

/* Map bubble entrance (per-bubble stagger via inline animation-delay) +
   a continuous ping ring on the selected bubble (NOT gated, keeps pulsing). */
.map-bubble { transform-box: fill-box; transform-origin: center; }
#app[data-anim~="data"] .map-bubble { animation: bubbleIn .5s ease both; }
@keyframes bubbleIn {
  0%   { transform: scale(0);    opacity: 0; }
  70%  { transform: scale(1.08); opacity: 1; }
  100% { transform: scale(1); }
}
.map-pulse { transform-box: fill-box; transform-origin: center; animation: pulseRing 1.6s ease-out infinite; }
@keyframes pulseRing {
  0%   { transform: scale(1);   opacity: .7; }
  100% { transform: scale(1.9); opacity: 0; }
}

/* Donut sweep on data/view renders. */
.donut-arc { transform-box: fill-box; transform-origin: center; }
#app[data-anim~="data"] .donut-arc { animation: fadeIn .6s ease both; }

/* Modal (drill-down) pop. Not gated — fires whenever the modal mounts. */
.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); }
}
.modal-back { animation: fadeIn .16s ease both; }

/* 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;
  }
}
