/* ============================================
   Component: Chart
   Partial: parts/_chart.html.erb
   Stimulus: chart_controller.js
   ============================================ */
/* The series tokens, --color-series-1 to --color-series-8, are pal-e-design's: its gem, pal_e_design, defines them
   for light and for dark, and the layout links its tokens before this file. They are a validated categorical
   palette's eight, in its order. On the host's surfaces, in both schemes, each pair of neighbours stays apart for
   colour-blind readers. Three light ones fall under 3:1 against the surface, which each chart's legend and table
   relieve. A series takes them in its order, a treemap's categories past the eighth take --color-muted, and a
   highlighted bar takes its level's colour (charts › SOP 7). */

/* The chart part (parts/_chart.html.erb, #384), drawn by our own library (lib/chart), and its tap
   (controllers/chart_controller.js). The chart lays itself out by its own width, not the page's: it is a
   container, and the part writes the container queries that thin and turn its labels. Its plot is a grid's
   middle column, its axes beside and below it, so the plot stretches and its text keeps its size. The image
   sets no colour of its own, so its text, ticks and rules take currentColor, the colour of the text the chart
   sits in (charts › SOP 7); every mark takes a token by name. */
.chart {
  container-type: inline-size;
  display: grid;
  gap: var(--spacing-sm);
  margin: 0 0 var(--spacing-lg);
}

.chart-description { color: var(--color-muted); font-size: var(--font-size-sm); }

/* The body takes the space above the image, so the frames, placed in it, sit at the plot's top by the
   layout's top alone */
.chart-body { position: relative; margin-top: var(--spacing-sm); }

.chart-image {
  display: grid;
}

.chart-image svg { overflow: visible; display: block; }
/* Row 1 is the room the value axis's top label takes above the plot (Chart::Layout#top) */
.chart-axis-left { grid-column: 1; grid-row: 2; }
.chart-plot { grid-column: 2; grid-row: 2; }
.chart-axis-bottom { grid-column: 2; grid-row: 3; }

.chart-tick-text, .chart-label {
  fill: currentColor;
  font-size: var(--font-size-xs);
  font-variant-numeric: tabular-nums;
}

.chart-tick-mark { stroke: currentColor; }
.chart-rule { stroke: currentColor; stroke-opacity: 0.15; }
.chart-rule-zero { stroke-opacity: 1; }

.chart-bar, .chart-point { stroke: var(--color-surface); stroke-width: 1px; }
.chart-line { fill: none; stroke-width: 2px; }
/* Progress's plan, a rule across its bar, drawn over it in currentColor, the colour of the text the chart sits
   in (charts › SOP 7); a tap on it reaches its bar, or the gap a figure with no value leaves */
.chart-against-rule { stroke: currentColor; stroke-width: 2px; pointer-events: none; }
/* A line's point's hit circle and a figure with no value's gap (#549) take a tap and draw nothing */
.chart-hit { fill: transparent; }
.chart-part { cursor: pointer; }

/* A treemap tile's label, its category then its value, haloed in the surface's colour so it reads on any tile.
   The halo, as the marks' outline, assumes the chart sits on the host's surface or its page. */
.chart-label {
  paint-order: stroke;
  stroke: var(--color-surface);
  stroke-width: 3px;
  stroke-linejoin: round;
}

.chart-label-value { font-weight: 600; }

/* A mark's colour, a legend swatch's too, by its token's name */
.chart-fill-series-1 { fill: var(--color-series-1); background-color: var(--color-series-1); }
.chart-fill-series-2 { fill: var(--color-series-2); background-color: var(--color-series-2); }
.chart-fill-series-3 { fill: var(--color-series-3); background-color: var(--color-series-3); }
.chart-fill-series-4 { fill: var(--color-series-4); background-color: var(--color-series-4); }
.chart-fill-series-5 { fill: var(--color-series-5); background-color: var(--color-series-5); }
.chart-fill-series-6 { fill: var(--color-series-6); background-color: var(--color-series-6); }
.chart-fill-series-7 { fill: var(--color-series-7); background-color: var(--color-series-7); }
.chart-fill-series-8 { fill: var(--color-series-8); background-color: var(--color-series-8); }
.chart-fill-muted { fill: var(--color-muted); background-color: var(--color-muted); }
.chart-fill-good { fill: var(--color-success); background-color: var(--color-success); }
.chart-fill-warning { fill: var(--color-warning); background-color: var(--color-warning); }
.chart-fill-bad { fill: var(--color-danger); background-color: var(--color-danger); }
.chart-stroke-series-1 { stroke: var(--color-series-1); }
.chart-stroke-series-2 { stroke: var(--color-series-2); }
.chart-stroke-series-3 { stroke: var(--color-series-3); }
.chart-stroke-series-4 { stroke: var(--color-series-4); }
.chart-stroke-series-5 { stroke: var(--color-series-5); }
.chart-stroke-series-6 { stroke: var(--color-series-6); }
.chart-stroke-series-7 { stroke: var(--color-series-7); }
.chart-stroke-series-8 { stroke: var(--color-series-8); }
.chart-stroke-muted { stroke: var(--color-muted); }

/* A part's frame, opened by a tap: over the plot, on the side of its part with more room. Its box takes no
   tap, so a tap on a part beneath an open frame reaches that part and moves the frame there; only its records
   link takes one (#384). The part sets its top, the plot's. */
.chart-frames {
  position: absolute;
  pointer-events: none;
}

/* A frame paints its own ground, so its words take the host's text token on its own surface, not the colour
   of the text the chart sits in */
.chart-frame {
  position: absolute;
  display: grid;
  gap: var(--spacing-xs);
  width: max-content;
  max-width: 50%;
  padding: var(--spacing-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--font-size-sm);
  pointer-events: none;
}

.chart-frame[hidden] { display: none; }

/* The axes and the plot's labels take no tap either: they're drawn over the parts, so a tap on a part's own
   words, or on the part of it that reaches over an axis, reaches the part (#384) */
.chart-axis, .chart-label { pointer-events: none; }
.chart-frame-value { font-weight: 600; font-variant-numeric: tabular-nums; }
.chart-frame-meta { color: var(--color-muted); font-size: var(--font-size-xs); }
.chart-frame-drawn, .chart-frame-against { font-variant-numeric: tabular-nums; }

.chart-frame-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-target);
  color: var(--color-link);
  pointer-events: auto;
}

/* The legend: each series, or a highlight, in words beside its colour; it doesn't toggle */
.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spacing-xs) var(--spacing-md);
  list-style: none;
  font-size: var(--font-size-sm);
}

.chart-legend-entry { display: inline-flex; align-items: center; gap: var(--spacing-xs); }

.chart-swatch {
  display: inline-block;
  width: 0.8em;
  height: 0.8em;
  border-radius: var(--radius-sm);
}

/* Progress's plan's swatch: a rule, in currentColor, the colour of the text the chart sits in, as the plan is
   drawn (charts › SOP 7) */
.chart-swatch-rule {
  width: 0;
  border-left: 2px solid currentColor;
  border-radius: 0;
}

/* The image's description: a copy of its table's text, a paragraph to a line, outside the tap, so a screen
   reader always has the chart's numbers. It's hidden from sight only by clipping, never by hidden or
   display: none, so it stays in the accessibility tree (#519). */
.chart-table-text {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Its table, last, behind a tap, closed, whatever its length (D-105, #519): <details> opens with no
   JavaScript and no hover, and is drawn as the figure part's other ways are. The size and the colour go on
   the summary alone, so the table inside keeps the table part's own size and colours. */
.chart-table-tap > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 var(--spacing-sm);
  min-height: var(--tap-target);
  color: var(--color-link);
  font-size: var(--font-size-xs);
  cursor: pointer;
  list-style: none;
}

/* A flex summary loses its marker, so it draws its own: closed, then open */
.chart-table-tap > summary::-webkit-details-marker { display: none; }
.chart-table-tap > summary::before { content: "▸"; }
.chart-table-tap[open] > summary::before { content: "▾"; }
