/* ==========================================================================
   DarkWeb Monitor — charts and the statistics dashboard
   --------------------------------------------------------------------------
   Loaded last, after pages.css. Two jobs:

     1. Publish the chart palette. charts.js reads --chart-* off <html> with
        getComputedStyle and re-reads it on every theme change, which is what
        stops a chart from carrying its own colours.
     2. Lay out /stats.

   Nothing here declares a raw colour. Every --chart-* value is an alias of a
   token from tokens.css.

   The palette aliases are deliberately plain var() references and never
   color-mix(): an unregistered custom property computes to its substituted
   token stream, so var(--cyan) resolves to a literal colour that ECharts can
   parse, while color-mix(...) would come back as an unresolved expression and
   silently break every series. Derived shades — tints, alpha, gradients — are
   mixed in charts.js from a value that did resolve.
   ========================================================================== */

:root {
    /* Categorical ramp. Nine separable hues; charts.js extends it with a pale
       and a deep pass when a chart has more series than that. */
    --chart-1: var(--cyan);
    --chart-2: var(--ok);
    --chart-3: var(--warn);
    --chart-4: var(--violet);
    --chart-5: var(--state-broken);
    --chart-6: var(--info);
    --chart-7: var(--bad);
    --chart-8: var(--blue);
    --chart-9: var(--muted);

    /* The two ends charts.js mixes towards when it derives a shade. */
    --chart-hi: var(--light);
    --chart-lo: var(--ink);

    /* Site state, the five values of `state`. Same tokens the chips use, so a
       category is the same colour in a chart as it is in a badge. */
    --chart-alive:   var(--state-alive);
    --chart-blocked: var(--state-blocked);
    --chart-broken:  var(--state-broken);
    --chart-dead:    var(--state-dead);
    --chart-waiting: var(--state-waiting);

    /* Semantic. */
    --chart-ok:       var(--ok);
    --chart-warn:     var(--warn);
    --chart-bad:      var(--bad);
    --chart-info:     var(--info);
    --chart-violet:   var(--violet);
    --chart-accent:   var(--accent);
    --chart-accent-2: var(--accent-2);
    --chart-muted:    var(--muted);

    /* Chrome: type, axes, gridlines, the surface a chart sits on. */
    --chart-text:      var(--text);
    --chart-dim:       var(--text-dim);
    --chart-faint:     var(--text-faint);
    --chart-line:      var(--border-strong);
    --chart-split:     var(--border);
    --chart-surface:   var(--surface);
    --chart-surface-2: var(--surface-2);
}

/* --------------------------------------------------------------- utility --- */

/* The accessible figures table under every chart. base.css already defines
   .sr-only; this is the name the statistics markup uses, declared here so the
   two never drift apart in only one of the two files. */
.visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* Without JavaScript there is no chart, so the numbers stop hiding. app.js
   puts .has-js on <html> as its first act. */
html:not(.has-js) .chart { display: none; }
html:not(.has-js) .chart-figures {
    position: static !important;
    width: auto; height: auto;
    margin: .8rem 0 0;
    overflow: visible;
    clip: auto;
    clip-path: none;
    white-space: normal;
}

/* ------------------------------------------------------------- dashboard --- */

.stats {
    display: flex;
    flex-direction: column;
    gap: clamp(1rem, 2.4vw, 1.6rem);
}

.stats__head { max-width: 62ch; }
.stats__head h1 { margin: .3rem 0 .5rem; }
.stats__head .eyebrow a { color: inherit; }

.stats__lede {
    margin: 0;
    color: var(--text-dim);
    font-size: .95rem;
    line-height: 1.65;
}

.stats__counters { margin: 0; }

.stats__nav {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    padding: .55rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    position: sticky;
    top: calc(var(--header-h) + .4rem);
    z-index: 4;
}
.stats__nav .chip { text-decoration: none; }

.stats__panels {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 30rem), 1fr));
    gap: clamp(.9rem, 2vw, 1.3rem);
    align-items: start;
}
.stats__panels--single { display: block; }

/* The wide ones. A Sankey, a year-long calendar, four years of growth, a
   treemap and a force layout all need the whole row; squeezing them into a
   half column is what makes a dashboard unreadable. The four that are left
   pair off two to a row, which is why DWM\Stats orders them the way it does. */
.chart-panel--flow,
.chart-panel--uptime,
.chart-panel--growth,
.chart-panel--tech,
.chart-panel--graph { grid-column: 1 / -1; }

.stats__pager {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: .6rem;
}
.stats__pager > :first-child { justify-self: start; }
.stats__pager > :last-child { justify-self: end; }

/* ----------------------------------------------------------- chart panel --- */

.chart-panel { scroll-margin-top: calc(var(--header-h) + 4.5rem); }

.chart-panel .panel__body {
    display: flex;
    flex-direction: column;
    gap: .7rem;
}

.chart-panel__flag { flex: none; }

.chart-lede,
.chart-summary {
    margin: 0;
    color: var(--text-dim);
    font-size: .82rem;
    line-height: 1.55;
}
.chart-summary {
    font-family: var(--font-mono);
    font-size: .78rem;
    color: var(--text);
}

.chart-note {
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    margin: 0;
    padding: .6rem .75rem;
    background: color-mix(in srgb, var(--warn) 8%, transparent);
    border: 1px solid color-mix(in srgb, var(--warn) 26%, transparent);
    border-radius: var(--radius-sm);
    color: var(--text-dim);
    font-size: .76rem;
    line-height: 1.6;
}
.chart-note .icon { color: var(--warn-ink); flex: none; margin-top: .15rem; }

.chart-source {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
}
.chart-foot-summary {
    margin-left: auto;
    color: var(--text-faint);
    font-family: var(--font-body);
    text-align: right;
}

/* --------------------------------------------------------- chart canvas --- */

/*
 * Heights are declared here and never inline: the CSP forbids a style
 * attribute, and ECharts needs a laid-out box before it can size a canvas.
 */
.chart {
    width: 100%;
    height: 340px;
    min-height: 220px;
}
.chart[data-chart-kind='sankey']   { height: 520px; }
.chart[data-chart-kind='sunburst'] { height: 420px; }
.chart[data-chart-kind='calendar'] { height: 260px; }
.chart[data-chart-kind='growth']   { height: 400px; }
.chart[data-chart-kind='treemap']  { height: 440px; }
.chart[data-chart-kind='graph']    { height: 480px; }
.chart[data-chart-kind='churn']    { height: 340px; }

.chart-panel--lg .chart { height: min(76vh, 760px); }
.chart-panel--lg .chart[data-chart-kind='calendar'] { height: min(46vh, 380px); }

/* A dataset that threw still leaves the panel intact and says so. */
.chart[data-chart-failed]::after {
    content: 'This chart could not be drawn. The figures below it are complete.';
    display: block;
    padding: 1.2rem;
    color: var(--text-faint);
    font-family: var(--font-mono);
    font-size: .76rem;
}

@media (max-width: 640px) {
    .chart { height: 300px; }
    .chart[data-chart-kind='sankey'] { height: 560px; }
    .chart[data-chart-kind='graph'] { height: 380px; }
    .chart-panel--lg .chart { height: 68vh; }
    .stats__nav { position: static; }
}

/* --------------------------------------------------------- empty states --- */

.chart-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    padding: clamp(1.4rem, 5vw, 3rem) 1rem;
    text-align: center;
    background:
        radial-gradient(circle at 50% 0%, var(--grid), transparent 70%),
        var(--surface-2);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius);
}
.chart-empty__art {
    width: min(220px, 60%);
    height: auto;
    opacity: .8;
}
.chart-empty__title {
    margin: .4rem 0 0;
    font-family: var(--font-display);
    font-size: 1rem;
    color: var(--text);
}
.chart-empty__note {
    margin: 0;
    max-width: 52ch;
    color: var(--text-dim);
    font-size: .8rem;
    line-height: 1.6;
}

/* -------------------------------------------------------------- tooltip --- */

/*
 * charts.js returns a real element from every ECharts formatter rather than an
 * HTML string, because the CSP is style-src 'self' and a style attribute
 * written through innerHTML would be dropped. That element is styled here; the
 * only thing JS sets is the swatch colour, through the CSSOM.
 */
.chart-tip {
    display: flex;
    flex-direction: column;
    gap: .18rem;
    padding: .5rem .65rem;
    min-width: 8rem;
    font-family: var(--font-mono);
    font-size: .72rem;
    line-height: 1.45;
    color: var(--text-dim);
}
.chart-tip__head {
    margin-bottom: .2rem;
    padding-bottom: .28rem;
    border-bottom: 1px solid var(--border);
    color: var(--text);
    font-weight: 600;
    letter-spacing: .01em;
}
.chart-tip__row {
    display: flex;
    align-items: center;
    gap: .4rem;
}
.chart-tip__dot {
    width: 9px;
    height: 9px;
    flex: none;
    border-radius: 2px;
    background: var(--muted);
}
.chart-tip__label { color: var(--text-dim); }
.chart-tip__value {
    margin-left: auto;
    padding-left: .8rem;
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------- figures tables --- */

.table--figures { font-size: .78rem; }
.table--figures caption {
    caption-side: top;
    padding: .4rem 0 .6rem;
    color: var(--text-dim);
    font-family: var(--font-mono);
    font-size: .72rem;
    text-align: left;
}

@media print {
    /* On paper there is no canvas worth reproducing, but the figures matter. */
    .chart, .stats__nav, .stats__pager, .panel__actions { display: none !important; }
    .chart-figures {
        position: static !important;
        width: auto; height: auto;
        clip: auto; clip-path: none;
        white-space: normal;
    }
}
