/* ==========================================================================
   IntegrityShield — Server-rendered SVG charts + Nigeria tile map
   No JS charting library: controllers emit inline SVG, these classes skin it.
   Reads app.css tokens; safe in light, dark, and print.
   ========================================================================== */

/* ---- Chart frame ---- */
.chart { display: block; width: 100%; }
.chart--line  { height: 220px; }
.chart--bars  { height: 200px; }
.chart--donut { display: block; width: auto; max-width: 220px; margin: 0 auto; height: auto; }
.chart--spark { height: 44px; }
.chart-box { position: relative; min-width: 0; }
.chart-box--scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.chart-box--scroll .chart { min-width: 480px; }

/* ---- Grid, axes, labels ---- */
.chart__grid { stroke: var(--border); stroke-width: 1; shape-rendering: crispEdges; }
.chart__grid--zero { stroke: var(--border-strong); }
.chart__axis { stroke: var(--border-strong); stroke-width: 1; shape-rendering: crispEdges; }
.chart__lbl {
  font-family: var(--font); font-size: 10.5px; fill: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.chart__lbl--faint { fill: var(--text-faint); }
.chart__lbl--strong { fill: var(--text); font-weight: 700; font-size: 12px; }
.chart__val {
  font-family: var(--font); font-size: 10.5px; font-weight: 700; fill: var(--text);
  font-variant-numeric: tabular-nums;
}

/* ---- Line / area series ---- */
.chart__line {
  fill: none; stroke: var(--green-600); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke;
}
.chart__line--gold    { stroke: var(--gold); }
.chart__line--info    { stroke: var(--info); }
.chart__line--danger  { stroke: var(--danger); }
.chart__line--flagged { stroke: var(--flagged); }
.chart__line--muted   { stroke: var(--slate-400); stroke-dasharray: 4 4; }
.chart__area { fill: var(--green-600); fill-opacity: .1; stroke: none; }
.chart__area--gold { fill: var(--gold); fill-opacity: .1; }
.chart__area--info { fill: var(--info); fill-opacity: .1; }
.chart__dot { fill: var(--surface); stroke: var(--green-600); stroke-width: 2; }
.chart__dot--gold { stroke: var(--gold); }
.chart__dot--hot { fill: var(--green-600); r: 4; }

/* ---- Bars ---- */
.chart__bar { fill: var(--green-600); rx: 3; transition: fill-opacity .15s; fill-opacity: .92; }
.chart__bar:hover { fill-opacity: 1; }
.chart__bar--gold    { fill: var(--gold); }
.chart__bar--info    { fill: var(--info); }
.chart__bar--warning { fill: var(--warning); }
.chart__bar--danger  { fill: var(--danger); }
.chart__bar--flagged { fill: var(--flagged); }
.chart__bar--muted   { fill: var(--slate-300); }
.chart__bar--track   { fill: var(--slate-100); }

/* ---- Donut ---- */
.chart__track { fill: none; stroke: var(--slate-100); }
.chart__seg { fill: none; transition: stroke-width .15s; stroke-linecap: butt; }
.chart__seg:hover { stroke-width: 14; }
.chart__seg--s1 { stroke: var(--green-600); }
.chart__seg--s2 { stroke: var(--gold); }
.chart__seg--s3 { stroke: var(--info); }
.chart__seg--s4 { stroke: var(--flagged); }
.chart__seg--s5 { stroke: var(--warning); }
.chart__seg--s6 { stroke: var(--danger); }
.chart__seg--muted { stroke: var(--slate-300); }
.chart__donut-top { font-family: var(--font); font-size: 21px; font-weight: 800; fill: var(--text); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.chart__donut-sub { font-family: var(--font); font-size: 9.5px; fill: var(--text-muted); text-transform: uppercase; letter-spacing: .05em; }

/* ---- Sparkline (stat cards, table cells) ---- */
.chart__spark-line { fill: none; stroke: var(--green-500); stroke-width: 2; vector-effect: non-scaling-stroke; }
.chart__spark-area { fill: var(--green-500); fill-opacity: .12; }
.chart__spark-line--down { stroke: var(--danger); }
.chart__spark-area--down { fill: var(--danger); fill-opacity: .1; }

/* ---- Legend ---- */
.chart-legend {
  display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: center;
  margin-top: 12px; font-size: 12px; color: var(--text-muted);
}
.chart-legend--center { justify-content: center; }
.chart-legend span { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.chart-legend b { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; }
.chart-legend__swatch {
  width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; display: inline-block;
  background: var(--green-600);
}
.chart-legend__swatch--s1 { background: var(--green-600); }
.chart-legend__swatch--s2 { background: var(--gold); }
.chart-legend__swatch--s3 { background: var(--info); }
.chart-legend__swatch--s4 { background: var(--flagged); }
.chart-legend__swatch--s5 { background: var(--warning); }
.chart-legend__swatch--s6 { background: var(--danger); }
.chart-legend__swatch--muted { background: var(--slate-300); }

/* ---- Chart header strip (title + period selector row) ---- */
.chart-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; flex-wrap: wrap; }
.chart-head__title { font-size: 13.5px; font-weight: 700; color: var(--text); }
.chart-head__meta { font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Nigeria tile map — 36 states + FCT on an 8-column cartogram grid.
   Server emits one .tilemap__tile per state (abbr + value), with
   .tilemap__tile--empty invisible placeholders shaping the geography.
   ========================================================================== */
.tilemap {
  display: grid; grid-template-columns: repeat(8, 1fr); gap: 4px;
  max-width: 560px; margin: 0 auto;
}
.tilemap__tile {
  aspect-ratio: 1; border-radius: 6px; position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 600; line-height: 1.25; text-align: center;
  cursor: pointer; user-select: none; transition: transform .15s, box-shadow .15s;
  color: var(--text-muted); text-decoration: none; min-width: 0; overflow: hidden;
}
a.tilemap__tile:hover { text-decoration: none; }
.tilemap__tile:hover {
  transform: translateY(-2px); box-shadow: var(--shadow-md); z-index: 2;
}
.tilemap__tile b { font-size: 11.5px; font-weight: 750; letter-spacing: .01em; display: block; }
.tilemap__tile small { font-size: 8.5px; font-weight: 600; opacity: .85; font-variant-numeric: tabular-nums; display: block; }
.tilemap__tile--empty {
  background: transparent !important; border: none !important; cursor: default;
  pointer-events: none; box-shadow: none !important;
}
/* Selected / focused state tile. The ring is offset into the 4px grid gap
   so it is drawn against the card surface, not against whichever heat step
   the tile happens to carry — no single colour clears 3:1 on both ends of
   the ramp. --gold-deep is 4.7:1 on light surface, 3.6:1 on dark. */
.tilemap__tile.is-active { outline: 2px solid var(--gold-deep); outline-offset: 2px; }

/* 5-step sequential emerald scale (0 = no data). The ramp itself is a
   theme-independent data encoding and keeps its literals; the LABEL on each
   step is chosen so the value stays readable (#fff was 2.4:1 on heat-3 and
   3.9:1 on heat-4). heat-4 darkens one notch to carry white at 4.7:1. */
.heat-0 { background: var(--slate-100); color: var(--text-faint); }
.heat-1 { background: #e3f4ec; color: #0a5c39; }
.heat-2 { background: #a8dcc3; color: #063b25; }
.heat-3 { background: #5cb98a; color: #063b25; }
.heat-4 { background: #0e8450; color: #fff; }
.heat-5 { background: #0a5c39; color: #fff; }
.heat-4 small, .heat-5 small { opacity: .82; }

/* Alert overlay — a state carrying open exceptions gets a flag dot. */
.tilemap__tile .tilemap__flag {
  position: absolute; top: 4px; right: 4px; width: 7px; height: 7px;
  border-radius: 50%; background: var(--flagged); box-shadow: 0 0 0 2px rgba(255, 255, 255, .65);
}

/* Heat legend — discrete swatch ramp under the map. */
.tilemap-legend {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin-top: 12px; font-size: 11px; color: var(--text-muted);
}
.tilemap-legend__ramp { display: flex; border-radius: 4px; overflow: hidden; }
.tilemap-legend__ramp i { width: 26px; height: 10px; display: block; }
.tilemap-legend__ramp i:nth-child(1) { background: #e3f4ec; }
.tilemap-legend__ramp i:nth-child(2) { background: #a8dcc3; }
.tilemap-legend__ramp i:nth-child(3) { background: #5cb98a; }
.tilemap-legend__ramp i:nth-child(4) { background: #0e8450; }
.tilemap-legend__ramp i:nth-child(5) { background: #0a5c39; }

/* Region summary chips beside the map */
.tilemap-side { display: flex; flex-direction: column; gap: 8px; }
.tilemap-side__row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font-size: 12.5px; color: var(--text-muted); padding: 7px 10px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
}
.tilemap-side__row b { color: var(--text); font-variant-numeric: tabular-nums; }

@media (max-width: 480px) {
  .tilemap { gap: 3px; }
  .tilemap__tile { border-radius: 4px; font-size: 8.5px; }
  .tilemap__tile b { font-size: 9.5px; }
  .tilemap__tile small { display: none; }
}

/* ---- Meter bar (utilisation inside chart cards) ---- */
.chart-meter { height: 8px; border-radius: 4px; background: var(--slate-100); overflow: hidden; }
.chart-meter__fill { height: 100%; border-radius: 4px; background: linear-gradient(90deg, var(--green-500), var(--green-700)); }
.chart-meter__fill--gold { background: linear-gradient(90deg, var(--gold-bright), var(--gold-deep)); }
.chart-meter__fill--warning { background: var(--warning); }
.chart-meter__fill--danger { background: var(--danger); }

/* ==========================================================================
   Dark theme corrections
   ========================================================================== */
html[data-theme="dark"] .chart__bar--track { fill: var(--slate-800); }
html[data-theme="dark"] .chart__bar--muted { fill: var(--slate-600); }
html[data-theme="dark"] .chart__track { stroke: var(--slate-800); }
html[data-theme="dark"] .chart__seg--muted { stroke: var(--slate-600); }
html[data-theme="dark"] .chart-legend__swatch--muted { background: var(--slate-600); }
html[data-theme="dark"] .chart-meter { background: var(--slate-800); }
html[data-theme="dark"] .heat-0 { background: var(--slate-800); color: var(--slate-400); }
html[data-theme="dark"] .heat-1 { background: rgba(18, 148, 92, .18); color: #7fd4a8; }
html[data-theme="dark"] .heat-2 { background: rgba(18, 148, 92, .34); color: #b7e6cf; }
html[data-theme="dark"] .tilemap__tile .tilemap__flag { box-shadow: 0 0 0 2px rgba(16, 29, 44, .8); }
/* The legend ramp must track the tiles it explains — steps 1 and 2 are
   re-declared above for dark, so their swatches follow. */
html[data-theme="dark"] .tilemap-legend__ramp i:nth-child(1) { background: rgba(18, 148, 92, .18); }
html[data-theme="dark"] .tilemap-legend__ramp i:nth-child(2) { background: rgba(18, 148, 92, .34); }

/* ==========================================================================
   Print — charts and the tile map keep their fills; hovers/motion drop.
   ========================================================================== */
@media print {
  .chart, .tilemap, .tilemap__tile, .chart__bar, .chart__seg, .chart__area,
  .chart-legend__swatch, .tilemap-legend__ramp i, .chart-meter__fill,
  .heat-0, .heat-1, .heat-2, .heat-3, .heat-4, .heat-5 {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .tilemap__tile { box-shadow: none !important; transform: none !important; cursor: default; }
  .tilemap__tile:hover { transform: none; box-shadow: none; }
  .heat-0 { border: 1px solid #e1e7ee; }
  .chart-box--scroll { overflow: visible; }
  .chart-box--scroll .chart { min-width: 0; }
  .chart__grid { stroke: #e1e7ee; }
  .chart__lbl { fill: #32405a; }
}

/* ==========================================================================
   HTML-composed chart furniture — no SVG needed.
   ========================================================================== */

/* ---- Ranking bars — "top LGAs by disbursement", "projects by burn" ---- */
.hbar-list { display: flex; flex-direction: column; gap: 12px; }
.hbar { display: grid; grid-template-columns: minmax(min(90px, 30%), 160px) 1fr auto; gap: 10px; align-items: center; font-size: 12.5px; }
.hbar__label { color: var(--text); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hbar__track { height: 10px; border-radius: 5px; background: var(--slate-100); overflow: hidden; }
.hbar__fill { height: 100%; border-radius: 5px; background: linear-gradient(90deg, var(--green-500), var(--green-700)); min-width: 2px; }
.hbar__fill--gold    { background: linear-gradient(90deg, var(--gold-bright), var(--gold-deep)); }
.hbar__fill--info    { background: var(--info); }
.hbar__fill--warning { background: var(--warning); }
.hbar__fill--danger  { background: var(--danger); }
.hbar__fill--flagged { background: var(--flagged); }
.hbar__val { color: var(--text); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; text-align: right; }
.hbar__sub { grid-column: 1 / -1; font-size: 11px; color: var(--text-faint); margin-top: -6px; }
@media (max-width: 480px) {
  .hbar { grid-template-columns: 1fr auto; }
  .hbar__label { grid-column: 1 / -1; }
}

/* ---- Stacked segment bar — one bar split by status (matched/pending/failed) */
.segbar { display: flex; height: 12px; border-radius: 6px; overflow: hidden; background: var(--slate-100); }
.segbar > i { display: block; height: 100%; min-width: 0; }
.segbar__s1 { background: var(--green-600); }
.segbar__s2 { background: var(--gold); }
.segbar__s3 { background: var(--info); }
.segbar__s4 { background: var(--warning); }
.segbar__s5 { background: var(--danger); }
.segbar__s6 { background: var(--flagged); }
.segbar__muted { background: var(--slate-300); }

/* ---- CSS column mini-chart (tiny trend blocks where SVG is overkill) ---- */
.colbars { display: flex; align-items: flex-end; gap: 3px; height: 64px; }
.colbars > i {
  flex: 1; min-width: 2px; display: block; border-radius: 2px 2px 0 0;
  background: var(--green-500); opacity: .85; transition: opacity .12s;
}
.colbars > i:hover { opacity: 1; }
.colbars > i.is-peak { background: var(--gold); }
.colbars > i.is-err { background: var(--danger); }

/* ---- Chart empty / no-data state ---- */
.chart-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  min-height: 160px; color: var(--text-faint); font-size: 12.5px; text-align: center;
}
.chart-empty svg { width: 26px; height: 26px; opacity: .6; }

/* ---- Two-up chart layout inside a card body ---- */
.chart-duo { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 22px; align-items: center; }
@media (max-width: 720px) { .chart-duo { grid-template-columns: 1fr; } }

/* ---- Dark + print corrections for HTML chart furniture ---- */
html[data-theme="dark"] .hbar__track { background: var(--slate-800); }
html[data-theme="dark"] .segbar { background: var(--slate-800); }
html[data-theme="dark"] .segbar__muted { background: var(--slate-600); }
@media print {
  .hbar__fill, .segbar > i, .colbars > i {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .hbar-list, .segbar, .colbars { page-break-inside: avoid; break-inside: avoid; }
}

/* ==========================================================================
   Device pass — charts scale rather than crush.
   The SVG figures are already fluid (width: 100% on a viewBox), so the risk
   at 360px is not overflow but illegibility: a 12-point series drawn into
   332px puts axis labels on top of each other. Where a figure genuinely
   needs room, the view wraps it in .chart-box--scroll and it scrolls inside
   its own box; everything else simply loses height and label weight.
   ========================================================================== */
.chart-box, .chart-legend, .hbar-list, .tilemap { min-width: 0; }
.chart { max-width: 100%; }

@media (max-width: 768px) {
  .chart--line { height: 200px; }
  .chart--bars { height: 180px; }
  /* Below the point where a 480px minimum still fits, the scrolling variant
     narrows to the viewport so it is the CHART that scrolls, not the page. */
  .chart-box--scroll .chart { min-width: 420px; }
}

@media (max-width: 560px) {
  .chart--line { height: 180px; }
  .chart--bars { height: 168px; }
  .chart--donut { max-width: 180px; }
  .chart__lbl { font-size: 9.5px; }
  .chart__val { font-size: 9.5px; }
  .chart__donut-top { font-size: 18px; }
  .chart-box--scroll .chart { min-width: 360px; }
  .chart-legend { gap: 5px 12px; font-size: 11.5px; }
  .chart-head { gap: 6px; }
  .tilemap-legend { flex-wrap: wrap; gap: 6px; }
  .tilemap-legend__ramp i { width: 20px; }
}

@media (pointer: coarse) {
  /* The 37-cell Nigeria cartogram is exempt from the 44px target rule: the
     grid is 8 columns wide because that IS the geography, and 8 × 44px needs
     388px of track inside a 332px page. Cells measure ~40px at 360px, above
     the WCAG 2.2 AA 24px floor, and every state is also reachable from the
     ranked list beside the map. Everything else here grows. */
  .tilemap-side__row { min-height: 44px; }
  a.hbar { min-height: 44px; align-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  .tilemap__tile:hover { transform: none; }
  .chart__bar, .chart__seg, .colbars > i { transition: none; }
}
