/*
 * Media Stats Updater — panels stats page.
 *
 * Enqueued by OSU_Template only on pages using the panels template. Plain CSS,
 * not Tailwind: the theme compiles Tailwind at BUILD time, so a utility class
 * invented in a template silently does nothing until that build is re-run. A
 * stylesheet of our own sidesteps that entirely.
 *
 * Colours come from the theme's daisyUI tokens, so the page follows the site
 * rather than being pinned to hex values.
 */

/*
 * Dropping get_sidebar() is not enough on its own — the theme sizes and floats
 * .content-area for a two-column layout, so the column stays narrow with empty
 * space beside it. Scoped with :has() so only the page actually containing this
 * template widens.
 */
.content-area:has(.osu-panels) {
    width: 100%;
    float: none;
}

.osu-panels {
    /*
     * Border colours are SOLID mixes, not alpha fades. Fading --bc (the text
     * colour, chroma 0.019) over a dark background yields grey; mixing --p into
     * --b3 keeps the violet cast at full opacity. Same formulas the site's own
     * panels use, redeclared here so this does not depend on another plugin's
     * stylesheet.
     */
    --osu-panel-border: color-mix(in oklch, oklch(var(--b3, 23.03% 0.046 296)), oklch(var(--p, 52.5665% 0.136823 308.4776)) 22%);
    --osu-rule:         color-mix(in oklch, oklch(var(--b2, 18.67% 0.0288 300)), oklch(var(--bc, 93.8% 0.019 299)) 10%);
    --osu-mono:         "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

    max-width: 1192px;
    margin: 0 auto;
    padding: 32px 20px 64px;
    /*
     * --font-* are theme tokens (jrpg-design.css). Same expressions and
     * fallbacks the site's own panels use, so this follows the theme instead of
     * pinning font names that could drift out of sync.
     */
    font-family: var(--font-body, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
}

/*
 * Headings are re-asserted with a page-scoped selector (0,2,0). The theme's
 * `.site-content h1/h2` rules are (0,1,1) and outrank a bare class, which
 * silently strips the display font off both titles.
 */
.osu-panels .osu-panels__title {
    font-family: var(--font-display, "Audiowide", sans-serif);
    font-size: clamp(32px, 5vw, 44px);
    font-weight: 400;
    letter-spacing: -0.15px;
    color: oklch(var(--bc, 93.8% 0.019 299));
    margin: 0 0 8px;
}

.osu-panels__grid {
    display: grid;
    /*
     * min() is load-bearing, not decoration.
     *
     * A bare minmax(320px, 1fr) track can never be narrower than 320px —
     * auto-fit collapses empty tracks but will not shrink one below its minimum.
     * On a viewport under that, the grid overflows and the whole page scrolls
     * sideways. min(…, 100%) caps the minimum at whatever space actually exists.
     */
    grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
    gap: 18px;
}

/*
 * Chart runs get a wider minimum: two across at full width, one below roughly
 * 900px. Stat runs keep the narrower 320px track above.
 */
.osu-panels__grid--charts {
    grid-template-columns: repeat(auto-fit, minmax(min(440px, 100%), 1fr));
}

/* Separate elements, so the space between them is a margin — grid gap only
   applies within a grid. */
.osu-panels__grid + .osu-panels__grid { margin-top: 18px; }

.osu-panels__panel {
    background: oklch(var(--b2, 18.67% 0.0288 300));
    border: 1px solid var(--osu-panel-border);
    border-radius: 12px;
    padding: 16px 20px 18px;
}

.osu-panels .osu-panels__panel-title {
    font-family: var(--font-display, "Audiowide", sans-serif);
    font-size: 19px;
    font-weight: 400;
    color: oklch(var(--bc, 93.8% 0.019 299));
    margin: 0 0 12px;
}

.osu-panels__rows { margin: 0; }

/* Unticking a chart panel's number in Display leaves this container empty, and
   the rule below would then draw with nothing above it. */
.osu-panels__rows:empty { display: none; }

.osu-panels__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding: 9px 0;
    border-top: 1px solid var(--osu-rule);
}
.osu-panels__row:first-child { border-top: 0; }

.osu-panels__label {
    font-family: var(--osu-mono);
    font-size: 10.5px;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: oklch(var(--bc, 93.8% 0.019 299) / 0.55);
}

.osu-panels__value {
    font-family: var(--osu-mono);
    font-size: 15px;
    font-weight: 700;
    color: oklch(var(--bc, 93.8% 0.019 299));
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* The first value in a chart panel is the headline figure. */
.osu-panels__panel--chart .osu-panels__row:first-child .osu-panels__value { font-size: 26px; }

/* The rows above a chart are a different metric from the series below, so they
   get a rule to stop the two reading as one unit. */
.osu-panels__panel--chart .osu-panels__rows {
    border-bottom: 1px solid var(--osu-rule);
    padding-bottom: 10px;
}

/*
 * Charts sit at the BOTTOM of their panel, so their baselines line up across a
 * row.
 *
 * Panels in a grid row stretch to match the tallest, and these hold different
 * numbers of stat rows — one for Games Played, four for YouTube. Left to flow
 * from the top, the two x-axes end up at different heights and the shorter panel
 * trails a void, which reads as something missing. margin-top:auto on the
 * caption pushes it and the chart below it down together, moving that space
 * between the two groups instead.
 *
 * Deliberately NOT letting the chart grow to fill: height changes perceived
 * slope, so a panel with fewer stats would render a taller, more
 * dramatic-looking line for no reason but its neighbour's row count. Safe
 * because run-grouping means a chart panel only ever sits beside another chart
 * panel.
 */
.osu-panels__panel--chart {
    display: flex;
    flex-direction: column;
}

/* Both are <p>, which the theme styles by element — hence the scoping. */
.osu-panels .osu-panels__chart-label {
    font-family: var(--osu-mono);
    font-size: 10.5px;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: oklch(var(--bc, 93.8% 0.019 299) / 0.55);
    margin: 16px 0 0;
}

/*
 * The charts wrapper, not the label, now carries the push-down.
 *
 * The label used to be a direct child of the panel, so margin-top:auto on it
 * consumed the panel's spare column height. Charts are wrapped now — so the
 * wrapper is the flex child, and the auto margin has to move with it or the
 * baselines stop aligning across a grid row.
 */
.osu-panels__panel--chart .osu-panels__charts { margin-top: auto; }

/*
 * A panel with more than one chart spans the whole grid and splits into two
 * equal columns: the stat rows and the first chart on the left, anything after
 * it on the right.
 *
 * Two charts stacked in a half-width panel run to roughly twice the height of
 * whatever sits beside them, which defeats the baseline alignment above. But
 * simply widening the panel was worse in its own way — a label/value row is
 * justified apart, so at full width "TOTAL VIEWS" and its figure ended up at
 * opposite ends of 1,150px with nothing between them. Splitting into columns
 * fixes both: the row is back to a readable width, and the pie gets a box that
 * suits its shape rather than a third of a row.
 */
.osu-panels__panel--wide { grid-column: 1 / -1; }

.osu-panels__charts {
    display: flex;
    flex-direction: column;
}

.osu-panels__cols {
    display: flex;
    align-items: stretch;
    gap: 28px;
}

/*
 * Equal halves, and min-width:0 on both.
 *
 * Without that override a flex item refuses to shrink below its content's
 * intrinsic width, and a canvas reports the width it was last drawn at — so the
 * columns would grow on every resize and never shrink back, pushing the panel
 * wider than its grid cell.
 */
.osu-panels__col {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

/*
 * The aside column FILLS its height rather than sitting at the bottom.
 *
 * It was bottom-aligned so the two chart captions sat level, which looked tidy
 * and wasted the space above: the left column is taller by however much its stat
 * rows occupy, so the pie was pinned to a 220px box with a band of empty panel
 * over it — and a pie in a box far wider than it is tall draws a circle sized by
 * the SHORTER dimension, so it came out small twice over.
 *
 * Filling the column costs the caption alignment. That is the better trade here:
 * one caption sitting higher than the other is barely noticeable, whereas the
 * undersized pie was the first thing anyone saw.
 */
/*
 * MUST stay after `.osu-panels__panel--chart .osu-panels__charts` above.
 *
 * Both selectors are (0,2,0), so nothing but source order decides which margin
 * wins. Moved earlier, the panel-level auto margin takes over again and pins the
 * pie to the bottom of its column with a band of empty panel above it — the
 * exact thing this override exists to undo, and silently, since the CSS stays
 * valid either way.
 */
.osu-panels__col--aside .osu-panels__charts {
    margin-top: 0;
    flex: 1 1 auto;
}

.osu-panels__col--aside .osu-panels__chart-block {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    /* Without this a flex child will not shrink below its content height. */
    min-height: 0;
}

.osu-panels__col--aside .osu-panels__chart {
    flex: 1 1 auto;
    height: auto;
    /* Never smaller than a normal chart, however short the panel gets. */
    min-height: 220px;
}

/* Side by side stops working long before the panel does; stack below this. */
@media (max-width: 860px) {
    .osu-panels__cols { flex-direction: column; gap: 0; }
}

.osu-panels .osu-panels__chart-empty {
    font-family: var(--osu-mono);
    font-size: 11px;
    color: oklch(var(--bc, 93.8% 0.019 299) / 0.38);
    margin: 10px 0 2px;
}

/*
 * The latest value, readable without hovering.
 *
 * DOM elements over the canvas rather than canvas drawing: text stays crisp at
 * any device pixel ratio, and it is styled here instead of hand-measured with
 * measureText. Positioned from the scales on every draw — see endMarker() in
 * stats-page-panels.js. Same approach the site's token chart uses, which is
 * where this look comes from.
 */
.osu-panels__end-dot {
    position: absolute;
    width: 8px;
    height: 8px;
    border-radius: 999px;
    transform: translate(-50%, -50%);
    pointer-events: none;
    z-index: 3;
}
.osu-panels__end-dot::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 999px;
    background: inherit;
    animation: osu-panels-pulse 1.6s ease-out infinite;
}
@keyframes osu-panels-pulse {
    0%   { transform: scale(1);   opacity: 0.5; }
    100% { transform: scale(3.4); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    /* The ring loops forever, which is exactly the kind of movement this
       preference exists to stop. Dropping it leaves the dot, which is what
       actually marks the point — nothing is lost. */
    .osu-panels__end-dot::after { display: none; }
}

.osu-panels__end-badge {
    position: absolute;
    /* Sits in the gutter the right-hand axis occupies, as the token chart's
       does — it may cover one axis label, which is why the pill is opaque. */
    transform: translate(10px, -50%);
    padding: 1px 6px;
    border-radius: 4px;
    font-family: var(--osu-mono);
    font-size: 11px;
    font-weight: 600;
    /* The page background, for contrast against the coloured pill. */
    color: oklch(var(--b1, 14.1% 0.0246 300));
    white-space: nowrap;
    pointer-events: none;
    z-index: 3;
}
@media (max-width: 640px) {
    /* No room beside the axis at this width; the tooltip still has it. */
    .osu-panels__end-badge { display: none; }
}

.osu-panels__chart {
    margin-top: 8px;
    /* Taller than a sparkline: there are axis labels on both sides and the dot
       texture needs vertical room to read as a gradient. */
    height: 220px;
    position: relative;
}

/* Chart.js unavailable: collapse to fit the note, and take the canvas out of the
   visual flow WITHOUT removing it — the standard visually-hidden pattern, so its
   aria-label still describes the series to a screen reader. */
.osu-panels__chart--unavailable { height: auto; }
.osu-panels__chart--unavailable canvas {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.osu-panels__empty {
    color: oklch(var(--bc, 93.8% 0.019 299) / 0.55);
    padding: 28px 0;
}
