/* ============================================================
   EduShieldID — components/charts.css
   Dependency-free data visualization: inline-SVG donut charts and
   CSS bar/trend charts. No JS charting library — every value is
   computed server-side and rendered directly, colors come from
   the same design tokens as everything else.
   ============================================================ */

/* A row of chart cards should read as one band. Without a floor, a card whose
   series is empty collapses to header height while its neighbours stay tall,
   and the row looks broken rather than merely quiet. The floor is a min, not a
   fixed height, so a populated chart still grows to whatever it needs. */
.chart-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  min-height: 320px;
}
/* The chart (or the empty state standing in for it) takes the space the header
   leaves, so it centres in the card instead of hugging the title. */
.chart-card > :last-child { flex: 1 1 auto; }

/* ── Donut ──────────────────────────────────────────────────── */
.donut-chart {
  position: relative;
  width: 160px;
  height: 160px;
  margin: 0 auto;
}
.donut-chart__svg {
  width: 100%;
  height: 100%;
  /* Rotates the stroke origin from 3 o'clock to 12 o'clock. 270deg is the
     same angle as -90deg. */
  transform: rotate(270deg);
}
.donut-chart__track,
.donut-chart__segment {
  fill: none;
  stroke-width: 10;
}
.donut-chart__track { stroke: var(--clr-bg); }
.donut-chart__segment {
  stroke-linecap: round;
  transition: stroke-dashoffset var(--transition-slow);
}
.donut-chart__segment--success { stroke: var(--clr-success); }
.donut-chart__segment--warning { stroke: var(--clr-warning); }
.donut-chart__segment--danger  { stroke: var(--clr-danger); }
.donut-chart__segment--info    { stroke: var(--clr-info); }
.donut-chart__center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.donut-chart__center strong { font-size: var(--text-2xl); line-height: 1; }
.donut-chart__center span { font-size: var(--text-xs); color: var(--clr-text-muted); }

/* ── Shared legend (donut + any future chart) ──────────────────── */
.chart-legend {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  justify-content: center;
  font-size: var(--text-sm);
  color: var(--clr-text-muted);
}
.chart-legend li { display: flex; align-items: center; gap: var(--sp-2); }
.chart-legend strong { color: var(--clr-text); margin-left: var(--sp-1); }
.chart-legend__swatch {
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  display: inline-block;
  flex-shrink: 0;
}
.chart-legend__swatch--success { background: var(--clr-success); }
.chart-legend__swatch--warning { background: var(--clr-warning); }
.chart-legend__swatch--danger  { background: var(--clr-danger); }
.chart-legend__swatch--info    { background: var(--clr-info); }

/* ── Trend / vertical bar chart ─────────────────────────────── */
.trend-chart {
  display: flex;
  align-items: flex-end;
  gap: var(--sp-1);
  height: 140px;
}
.trend-chart__col {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  min-width: 0;
  height: 100%;
}
.trend-chart__bar-track {
  flex: 1;
  width: 100%;
  display: flex;
  align-items: flex-end;
}
.trend-chart__bar {
  width: 100%;
  height: 0;
  min-height: 2px;
  background: var(--clr-primary);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  transition: height var(--transition-slow);
}

/* Bar heights in 5% steps, chosen by a `pct-N` class.

   This was `height: calc(var(--val) * 1%)` with `--val` set by a
   `style="--val:{{ bar.pct }}"` attribute on each bar. The app serves
   `Content-Security-Policy: default-src 'self'` with no 'unsafe-inline', so
   the browser refused every one of those attributes — which meant `--val`
   was never set, the calc fell back to 0, and the dashboard's verification
   trend chart rendered as a row of flat 2px stubs. It logged one CSP error
   per bar and looked like a chart with no data.

   Buckets rather than 101 rules: 5% is finer than anyone reads off a
   sparkline, and the template picks one with `{% widthratio %}`, which needs
   no custom filter. */
.trend-chart__bar.pct-0  { height: 0; }
.trend-chart__bar.pct-1  { height: 5%; }
.trend-chart__bar.pct-2  { height: 10%; }
.trend-chart__bar.pct-3  { height: 15%; }
.trend-chart__bar.pct-4  { height: 20%; }
.trend-chart__bar.pct-5  { height: 25%; }
.trend-chart__bar.pct-6  { height: 30%; }
.trend-chart__bar.pct-7  { height: 35%; }
.trend-chart__bar.pct-8  { height: 40%; }
.trend-chart__bar.pct-9  { height: 45%; }
.trend-chart__bar.pct-10 { height: 50%; }
.trend-chart__bar.pct-11 { height: 55%; }
.trend-chart__bar.pct-12 { height: 60%; }
.trend-chart__bar.pct-13 { height: 65%; }
.trend-chart__bar.pct-14 { height: 70%; }
.trend-chart__bar.pct-15 { height: 75%; }
.trend-chart__bar.pct-16 { height: 80%; }
.trend-chart__bar.pct-17 { height: 85%; }
.trend-chart__bar.pct-18 { height: 90%; }
.trend-chart__bar.pct-19 { height: 95%; }
.trend-chart__bar.pct-20 { height: 100%; }
.trend-chart__label {
  font-size: var(--text-xs);
  color: var(--clr-text-muted);
  white-space: nowrap;
}
/* Fourteen date labels do not fit under fourteen bars (they printed over
   each other). Show every third, counting so the newest bar is labelled;
   the rest keep their space so the bars stay on one baseline, and every bar
   still carries its own label and count in its title / aria-label. */
.trend-chart__col:not(:nth-last-child(3n+1)) .trend-chart__label {
  visibility: hidden;
}

.chart-empty {
  color: var(--clr-text-muted);
  font-size: var(--text-sm);
  text-align: center;
  padding: var(--sp-10) 0;
}

/* ── Responsive ─────────────────────────────────────────────── */
@media (max-width: 480px) {
  .donut-chart { width: 130px; height: 130px; }
  .trend-chart__label { display: none; }
  .trend-chart { height: 100px; }
}
