/* ==========================================================================
   UX primitives — skeleton loading, data visualization, empty states.

   Branch: feat/premium-dashboard-ux

   Every color here resolves through a --lt-* token defined in
   application.css, so both themes are covered by construction rather than
   by a trailing dark-mode override block. Do not introduce raw hex here.
   ========================================================================== */

/* --- Skeleton loading ---------------------------------------------------- */

.skeleton {
  display: block;
  background: var(--lt-skeleton-base);
  border-radius: var(--lt-radius-sm);
  position: relative;
  overflow: hidden;
}

.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--lt-skeleton-sheen) 50%,
    transparent 100%
  );
  transform: translateX(-100%);
}

@media (prefers-reduced-motion: no-preference) {
  .skeleton::after {
    animation: lt-skeleton-sheen 1.4s ease-in-out infinite;
  }
}

@keyframes lt-skeleton-sheen {
  100% {
    transform: translateX(100%);
  }
}

.skeleton-text {
  height: 0.85em;
  margin: 0.3em 0;
}

.skeleton-text:last-child {
  width: 60%;
}

.skeleton-title {
  height: 1.3em;
  width: 45%;
  margin-bottom: 10px;
}

.skeleton-value {
  height: 1.6em;
  width: 70%;
}

.skeleton-label {
  height: 0.7em;
  width: 50%;
  margin-bottom: 8px;
}

.skeleton-circle {
  border-radius: 999px;
  width: 40px;
  height: 40px;
}

.skeleton-button {
  height: 38px;
  width: 120px;
  border-radius: 999px;
}

.skeleton-card {
  border: 1px solid var(--lt-card-border);
  border-radius: var(--lt-radius-lg);
  background: var(--lt-card-bg);
  padding: 14px;
}

.skeleton-stack {
  display: grid;
  gap: 10px;
}

.skeleton-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--lt-border-muted);
}

.skeleton-row:last-child {
  border-bottom: 0;
}

.skeleton-row-main {
  flex: 1;
  min-width: 0;
}

/* --- Charts ---------------------------------------------------------------
   Bar charts are CSS/flow based rather than SVG: labels reflow with the page,
   there is no viewBox scaling to fight on a 360px phone, and the bars theme
   from the same tokens as everything else. Sparklines use inline SVG because
   a polyline genuinely needs it.
   ------------------------------------------------------------------------- */

.lt-chart {
  display: grid;
  gap: 10px;
  margin: 0;
}

.lt-chart-header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.lt-chart-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 800;
  color: var(--lt-text-strong);
}

.lt-chart-caption {
  margin: 0;
  font-size: 12px;
  color: var(--lt-chart-axis-text);
}

/* Vertical bars, one column per data point. */
.lt-bars {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  align-items: end;
  gap: clamp(3px, 1.2vw, 10px);
  height: var(--lt-chart-height, 160px);
  padding: 0 2px;
  border-bottom: 1px solid var(--lt-chart-grid);
}

.lt-bar {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: 100%;
  min-width: 0;
  gap: 4px;
}

.lt-bar-track {
  display: flex;
  align-items: flex-end;
  width: 100%;
  height: 100%;
}

.lt-bar-fill {
  width: 100%;
  min-height: 2px;
  height: var(--lt-bar-height, 0%);
  background: var(--lt-chart-series-1);
  border-radius: 4px 4px 0 0;
  transform-origin: bottom center;
}

@media (prefers-reduced-motion: no-preference) {
  .lt-bar-fill {
    animation: lt-bar-rise 620ms cubic-bezier(0.22, 1, 0.36, 1) backwards;
    animation-delay: var(--lt-bar-delay, 0ms);
  }
}

@keyframes lt-bar-rise {
  from {
    transform: scaleY(0);
  }
  to {
    transform: scaleY(1);
  }
}

/* Tone modifiers are declared once, after BOTH .lt-bar-fill and
   .lt-hbar-fill, further down this file. They are single-class selectors, so
   if they were declared here the later .lt-hbar-fill background would win the
   cascade and every toned horizontal bar would silently render green. */

/* Zero-value columns read as a faint stub so the axis does not look broken. */
.lt-bar-fill--empty {
  background: var(--lt-chart-track);
  height: 2px;
  animation: none;
}

.lt-bar-labels {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: clamp(3px, 1.2vw, 10px);
  padding: 0 2px;
}

.lt-bar-label {
  font-size: 10px;
  line-height: 1.2;
  text-align: center;
  color: var(--lt-chart-axis-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lt-bar-value {
  font-size: 10px;
  font-weight: 800;
  color: var(--lt-text-muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Horizontal bars, for ranked category lists such as top customers. */
.lt-hbars {
  display: grid;
  gap: 8px;
  margin: 0;
}

.lt-hbar {
  display: grid;
  grid-template-columns: minmax(70px, 30%) 1fr auto;
  align-items: center;
  gap: 10px;
}

.lt-hbar-label {
  font-size: 12px;
  color: var(--lt-text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lt-hbar-track {
  background: var(--lt-chart-track);
  border-radius: 999px;
  height: 10px;
  overflow: hidden;
}

.lt-hbar-fill {
  display: block;
  height: 100%;
  width: var(--lt-bar-width, 0%);
  background: var(--lt-chart-series-1);
  border-radius: 999px;
  transform-origin: left center;
}

@media (prefers-reduced-motion: no-preference) {
  .lt-hbar-fill {
    animation: lt-hbar-grow 620ms cubic-bezier(0.22, 1, 0.36, 1) backwards;
    animation-delay: var(--lt-bar-delay, 0ms);
  }
}

@keyframes lt-hbar-grow {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

/* --- Series tone modifiers ------------------------------------------------
   Declared here, after both .lt-bar-fill and .lt-hbar-fill, so a tone passed
   to either chart partial actually wins the cascade. Each selector is listed
   for both orientations rather than relying on one shared class name, which
   keeps the intent obvious at the call site.
   ------------------------------------------------------------------------- */

.lt-bar-fill--negative,
.lt-hbar-fill.lt-bar-fill--negative {
  background: var(--lt-chart-negative);
}

.lt-bar-fill--series-2,
.lt-hbar-fill.lt-bar-fill--series-2 {
  background: var(--lt-chart-series-2);
}

.lt-bar-fill--series-3,
.lt-hbar-fill.lt-bar-fill--series-3 {
  background: var(--lt-chart-series-3);
}

.lt-bar-fill--series-4,
.lt-hbar-fill.lt-bar-fill--series-4 {
  background: var(--lt-chart-series-4);
}

.lt-bar-fill--series-5,
.lt-hbar-fill.lt-bar-fill--series-5 {
  background: var(--lt-chart-series-5);
}

.lt-hbar-value {
  font-size: 12px;
  font-weight: 800;
  color: var(--lt-text-strong);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Sparkline, sized to sit inside a KPI tile.
   pathLength="1" is set as an attribute on the polyline so the dash animation
   below is resolution independent. */
.lt-sparkline {
  display: block;
  width: 100%;
  height: var(--lt-sparkline-height, 28px);
  overflow: visible;
}

.lt-sparkline-line {
  fill: none;
  stroke: var(--lt-chart-series-1);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.lt-sparkline-area {
  fill: var(--lt-chart-series-1);
  opacity: 0.14;
  stroke: none;
}

@media (prefers-reduced-motion: no-preference) {
  .lt-sparkline-line {
    stroke-dasharray: 1 1;
    animation: lt-sparkline-draw 900ms ease-out backwards;
  }

  .lt-sparkline-area {
    animation: lt-fade-in 700ms 280ms ease-out backwards;
  }
}

@keyframes lt-sparkline-draw {
  from {
    stroke-dashoffset: 1;
  }
  to {
    stroke-dashoffset: 0;
  }
}

@keyframes lt-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 0.14;
  }
}

.lt-chart-empty {
  display: grid;
  place-items: center;
  height: var(--lt-chart-height, 160px);
  border: 1px dashed var(--lt-empty-border);
  border-radius: var(--lt-radius-md);
  background: var(--lt-empty-bg);
  color: var(--lt-text-muted);
  font-size: 13px;
  text-align: center;
  padding: 12px;
}

/* --- Section heading ------------------------------------------------------
   .outstanding-list-title is used in six report views but was never defined
   anywhere in application.css, so every one of those headings fell back to
   the browser default h2 with its large margin. Defining it here styles the
   existing usages and the new chart sections consistently.
   ------------------------------------------------------------------------- */

.outstanding-list-title {
  margin: 0 0 8px;
  font-size: 0.95rem;
  font-weight: 800;
  color: var(--lt-text-strong);
}

/* --- Empty states -------------------------------------------------------- */

.empty-state-icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin: 0 auto 10px;
  border-radius: 999px;
  background: var(--lt-accent-bg);
  border: 1px solid var(--lt-accent-soft-border);
  color: var(--lt-brand-700);
  font-size: 20px;
  line-height: 1;
}

.empty-state-hint {
  margin: 10px 0 0;
  font-size: 12px;
  color: var(--lt-text-muted);
}

.empty-state--roomy {
  padding: 24px 16px;
}

/* The base rule in application.css is a centered inline-flex with no gap,
   which collides when an empty state offers both a primary and a secondary
   action. This file loads after application.css, so it wins. */
.empty-state-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}

/* --- Sparkline slot inside a KPI tile ------------------------------------ */

.stat-card-spark {
  margin-top: 8px;
}

/* --- Desktop sidebar -----------------------------------------------------
   Hidden by default. LawnTracker is mobile-first and the bottom nav is the
   primary navigation; this only takes over at desktop widths, where that
   fixed strip is wasted space. Everything lives inside the min-width query
   so no phone ever pays for these rules.
   ------------------------------------------------------------------------ */

.app-sidebar {
  display: none;
}

@media (min-width: 1024px) {
  :root {
    --lt-sidebar-width: 232px;
    --lt-sidebar-width-collapsed: 68px;
  }

  .app-sidebar {
    display: flex;
    flex-direction: column;
    gap: 14px;
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 40;
    width: var(--lt-sidebar-width);
    padding: 16px 12px;
    background: var(--lt-drawer-bg);
    border-right: 1px solid var(--lt-border);
    overflow-y: auto;
    overscroll-behavior: contain;
    transition: width 180ms ease;
  }

  .app-sidebar.is-collapsed {
    width: var(--lt-sidebar-width-collapsed);
  }

  @media (prefers-reduced-motion: reduce) {
    .app-sidebar {
      transition: none;
    }
  }

  /* Make room for the sidebar and reclaim it when collapsed.
     The topbar is position: sticky and spans the full body width, so it has
     to be offset as well or it slides underneath the fixed sidebar. */
  .app-body--signed-in .app-shell,
  .app-body--signed-in .app-topbar {
    margin-left: var(--lt-sidebar-width);
    transition: margin-left 180ms ease;
  }

  .lt-sidebar-collapsed .app-body--signed-in .app-shell,
  .lt-sidebar-collapsed .app-body--signed-in .app-topbar {
    margin-left: var(--lt-sidebar-width-collapsed);
  }

  @media (prefers-reduced-motion: reduce) {
    .app-body--signed-in .app-shell,
    .app-body--signed-in .app-topbar {
      transition: none;
    }
  }

  /* The bottom nav and its spacer are redundant once the sidebar is present. */
  .app-bottom-nav {
    display: none;
  }

  .app-shell--with-bottom-nav {
    padding-bottom: 32px;
  }

  .app-sidebar-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    min-height: 36px;
  }

  .app-sidebar-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: var(--lt-text-strong);
    font-weight: 800;
    min-width: 0;
  }

  .app-sidebar-brand-mark {
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    flex: 0 0 32px;
    border-radius: 10px;
    background: var(--lt-button-primary-bg);
    color: var(--lt-button-primary-text);
    font-size: 13px;
    letter-spacing: 0.02em;
  }

  .app-sidebar-brand-text {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .app-sidebar-toggle {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    flex: 0 0 30px;
    border-radius: 8px;
    border: 1px solid var(--lt-border);
    background: var(--lt-button-secondary-bg);
    cursor: pointer;
    padding: 0;
  }

  .app-sidebar-toggle:hover {
    background: var(--lt-button-secondary-hover);
  }

  .app-sidebar-toggle:focus-visible {
    outline: 3px solid var(--lt-focus-ring);
    outline-offset: 2px;
  }

  /* Chevron drawn in CSS so the sidebar needs no icon asset. */
  .app-sidebar-toggle-icon {
    width: 7px;
    height: 7px;
    border-left: 2px solid var(--lt-text-muted);
    border-bottom: 2px solid var(--lt-text-muted);
    transform: rotate(45deg);
    transition: transform 180ms ease;
  }

  .app-sidebar.is-collapsed .app-sidebar-toggle-icon {
    transform: rotate(-135deg);
  }

  .app-sidebar-nav {
    display: grid;
    gap: 4px;
    align-content: start;
  }

  .app-sidebar-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 2px;
  }

  .app-sidebar-heading {
    margin: 12px 0 2px;
    padding: 0 10px;
    font-size: 10px;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--lt-text-muted);
  }

  .app-sidebar-link {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    border-radius: var(--lt-radius-sm);
    text-decoration: none;
    color: var(--lt-text-primary);
    font-weight: 700;
    font-size: 14px;
    border: 1px solid transparent;
    white-space: nowrap;
  }

  .app-sidebar-link:hover {
    background: var(--lt-surface-muted);
    color: var(--lt-text-strong);
  }

  .app-sidebar-link:focus-visible {
    outline: 3px solid var(--lt-focus-ring);
    outline-offset: 2px;
  }

  .app-sidebar-link.is-active {
    background: var(--lt-nav-active-bg);
    border-color: var(--lt-nav-active-border);
    color: var(--lt-nav-active-text);
  }

  .app-sidebar-link--sub {
    font-weight: 600;
    font-size: 13px;
  }

  .app-sidebar-icon {
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    flex: 0 0 20px;
  }

  .app-sidebar-icon svg {
    width: 19px;
    height: 19px;
  }

  .app-sidebar-icon--dot::before {
    content: "";
    width: 5px;
    height: 5px;
    border-radius: 999px;
    background: currentColor;
    opacity: 0.55;
  }

  .app-sidebar-label {
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Collapsed: icons only. Labels and headings are removed from the
     accessibility tree too, and each link keeps a title attribute so the
     hover tooltip still names the destination. */
  .app-sidebar.is-collapsed .app-sidebar-brand-text,
  .app-sidebar.is-collapsed .app-sidebar-label,
  .app-sidebar.is-collapsed .app-sidebar-heading {
    display: none;
  }

  .app-sidebar.is-collapsed .app-sidebar-link {
    justify-content: center;
    padding-inline: 8px;
  }

  .app-sidebar.is-collapsed .app-sidebar-head {
    flex-direction: column;
    gap: 10px;
  }
}

/* --- Home revenue card --------------------------------------------------- */

.home-revenue-card {
  margin-top: 8px;
}

.home-revenue-card-link {
  display: block;
  color: inherit;
  text-decoration: none;
  border-radius: var(--lt-radius-md);
}

.home-revenue-card-link:hover,
.home-revenue-card-link:focus {
  color: inherit;
  text-decoration: none;
}

.home-revenue-card-link:focus-visible {
  outline: 3px solid var(--lt-focus-ring);
  outline-offset: 3px;
}
