/* Arcline — chart tokens.

   Opt-in. Import alongside tokens.css only in apps that draw charts:

     import "@arcline/react/styles/chart.css";

   Arcline deliberately ships no charting library. Requirements diverge too far
   between apps (real-time, 50k-point scatter, geo, candles) for one renderer to
   be right, and it would be dead weight for an app that needs a sparkline. What
   ships instead is the vocabulary: pick Recharts, visx, ECharts or raw SVG per
   app, feed it these variables, and every chart still reads as one system.

   The categorical palette is NOT hand-picked. Candidate hue sets and all 8!
   orderings were enumerated and measured with the dataviz validator; only
   orderings clearing every gate in BOTH modes were eligible, and Arc Blue was
   pinned to slot 1 among those. Results, on Arcline's own surfaces
   (light #F8F9F7, dark #171D20):

     worst adjacent CVD ΔE      9.6 light / 11.3 dark   (target ≥ 8)
     worst adjacent normal ΔE  17.7 light / 18.3 dark   (floor  ≥ 15)
     contrast vs surface       all 8 slots ≥ 3:1 in both modes

   Two hue families were tried and rejected, and both rejections are load-
   bearing: violet sits ΔE 2.2 from Arc Blue under simulated CVD, so it can
   never neighbour slot 1; clay/brown falls under the chroma floor and reads as
   grey, so it cannot carry identity at all.

   Re-derive after any change: node scripts/derive-chart-palette.mjs */

.arcline {
  /* ── Categorical: identity ──────────────────────────────────────────────
     Eight slots in FIXED order. Assign in sequence, never cycle. A ninth
     series is never a generated hue — fold it into "Other", facet into small
     multiples, or switch form. */
  /* Slot 1 is the signal, so it rides --brand-seed and rethemes with the app.
     Slots 2-8 do NOT: their hues and their order encode the colour-blind
     separation measured by scripts/derive-chart-palette.mjs, and no per-app
     seed may quietly reorder that. If a new seed lands on one of them,
     check-brand.mjs says so and the set has to be re-derived. */
  --chart-1: light-dark(oklch(from var(--_brand-seed) 0.508 calc(c * 0.9) h), oklch(from var(--_brand-seed) 0.67 calc(c * 0.71) calc(h + 3))); /* the system's signal */
  --chart-2: light-dark(#b85a3c, #cf6a3e); /* ember    */
  --chart-3: light-dark(#0f9c8a, #17a894); /* teal     */
  --chart-4: light-dark(#a8781f, #b8862a); /* yellow   */
  --chart-5: light-dark(#a83a6e, #d1568f); /* magenta  */
  --chart-6: light-dark(#526c12, #7fa322); /* lime     */
  --chart-7: light-dark(#4d38a5, #9280f0); /* violet   */
  --chart-8: light-dark(#1d7038, #3da35c); /* green    */

  /* ── Sequential: magnitude ──────────────────────────────────────────────
     One hue, light→dark, mode-invariant steps. For continuous encoding
     (heatmaps, choropleths) the lightest step may recede into the surface.
     For an ORDINAL ramp (funnel stages, tiers) the step nearest the surface
     must still clear 2:1, which means: start no lighter than 300 on light,
     and go no darker than 550 on dark.

     The ramp is the seed's hue at thirteen fixed lightnesses. It carries a
     slight rotation toward the pale end — the same drift the hand-picked blue
     ramp had — so the light steps stay distinguishable instead of collapsing
     into one wash. */
  --chart-seq-100: oklch(from var(--_brand-seed) 0.923 calc(c * 0.15) calc(h + 10));
  --chart-seq-150: oklch(from var(--_brand-seed) 0.88 calc(c * 0.24) calc(h + 8));
  --chart-seq-200: oklch(from var(--_brand-seed) 0.828 calc(c * 0.35) calc(h + 8));
  --chart-seq-250: oklch(from var(--_brand-seed) 0.771 calc(c * 0.48) calc(h + 6));
  --chart-seq-300: oklch(from var(--_brand-seed) 0.707 calc(c * 0.63) calc(h + 5));
  --chart-seq-350: oklch(from var(--_brand-seed) 0.643 calc(c * 0.78) calc(h + 4));
  --chart-seq-400: oklch(from var(--_brand-seed) 0.578 calc(c * 0.87) calc(h + 2));
  --chart-seq-450: oklch(from var(--_brand-seed) 0.556 calc(c * 1) h);
  --chart-seq-500: oklch(from var(--_brand-seed) 0.508 calc(c * 0.9) h);
  --chart-seq-550: oklch(from var(--_brand-seed) 0.45 calc(c * 0.78) h);
  --chart-seq-600: oklch(from var(--_brand-seed) 0.391 calc(c * 0.67) h);
  --chart-seq-650: oklch(from var(--_brand-seed) 0.333 calc(c * 0.55) h);
  --chart-seq-700: oklch(from var(--_brand-seed) 0.278 calc(c * 0.44) h);

  /* ── Diverging: polarity ────────────────────────────────────────────────
     The brand against ember — a cool and a warm pole that read as opposites —
     with a NEUTRAL GREY midpoint. Never a hue at the middle, never a rainbow.
     Intensity grows away from the midpoint in both modes.

     The cool pole is the sequential ramp, referenced rather than restated, so
     a reseeded brand carries through here too and the two ramps can never
     disagree about what the brand's dark step is. */
  --chart-div-cool-3: light-dark(var(--chart-seq-600), var(--chart-seq-200));
  --chart-div-cool-2: light-dark(var(--chart-seq-400), var(--chart-1));
  --chart-div-cool-1: light-dark(var(--chart-seq-250), var(--chart-seq-500));
  --chart-div-mid: light-dark(#e4e6e1, #2a3236);
  --chart-div-warm-1: light-dark(#eab39f, #a8532f);
  --chart-div-warm-2: light-dark(#cf7a5c, #cf6a3e);
  --chart-div-warm-3: light-dark(#8f4227, #e5a184);

  /* ── Status: state ──────────────────────────────────────────────────────
     Reserved meaning, never reused as "series 4". These are Arcline's own
     status tokens, so a chart and a badge agree. Always ship with an icon and
     a label — a status colour never carries meaning alone. Where a series
     shares a hue family with a status cue (chart-2 and critical are both
     ember), that pairing plus placement is what separates them, never hue. */
  --chart-status-good: var(--moss);
  --chart-status-caution: var(--yellow);
  --chart-status-critical: var(--ember);

  /* ── Chrome and ink ─────────────────────────────────────────────────────
     Text in a chart wears text tokens, never the series colour; a coloured
     mark beside a label is what carries identity. */
  --chart-surface: var(--surface);
  --chart-ink: var(--text);
  --chart-ink-secondary: var(--text-soft);
  --chart-ink-muted: var(--muted);
  --chart-grid: light-dark(#e4e7e2, #232b2f);
  --chart-axis: var(--rule-strong);
  --chart-tooltip-surface: var(--glass-surface-strong);
  --chart-annotation: var(--muted);
  /* Gap punched between adjoining fills so stacked segments stay countable. */
  --chart-mark-gap: 2px;
}

/* ── Texture ──────────────────────────────────────────────────────────────
   The accessibility channel, not decoration. One directional hatch used at 45°
   and its 135° mirror, tone-on-tone. Off by default; switch it on for print,
   for a reader who has asked for it, or automatically under forced-colors,
   where fills collapse to system colours and hue stops separating anything. */
.chart-texture-45 {
  background-image: repeating-linear-gradient(
    45deg,
    color-mix(in srgb, currentColor 55%, transparent) 0 1.5px,
    transparent 1.5px 5px
  );
}
.chart-texture-135 {
  background-image: repeating-linear-gradient(
    135deg,
    color-mix(in srgb, currentColor 55%, transparent) 0 1.5px,
    transparent 1.5px 5px
  );
}

@media (forced-colors: active) {
  /* Series fills all resolve to the same system colour here, so identity has
     to come from the hatch and from direct labels. */
  .chart-series { forced-color-adjust: none; }
}

/* ── Sparkline ────────────────────────────────────────────────────────────
   The one chart primitive worth shipping: pure SVG, no dependency, and it
   turns up in every dashboard. Anything larger belongs to the app's chosen
   renderer.

   The tone is carried by `color` on the svg, and every mark inherits it with
   currentColor. That indirection is load-bearing: the area is painted with a
   gradient, and a gradient stop can only reference currentColor — given the
   series hue on `fill` instead, the stops resolved to the inherited *text*
   colour and every area came out the same near-white wash. */
.sparkline { display: block; width: 100%; height: 40px; overflow: visible; color: var(--chart-1); }
.sparkline--positive { color: var(--chart-status-good); }
.sparkline--negative { color: var(--chart-status-critical); }
.sparkline__line { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.sparkline__area { stroke: none; }
.sparkline__point { fill: currentColor; }
/* A surface ring keeps the end marker legible where it overlaps the line. */
.sparkline__point--end { stroke: var(--chart-surface); stroke-width: 2; }
