/* Tokens. Light is the base set; dark is declared under both the media query
   and the explicit attribute so the attribute always wins. */
:root {
  --plane: #f9f9f7;
  --surface: #fcfcfb;
  --panel: #ffffff;
  --ink: #0b0b0b;
  --ink2: #52514e;
  --muted: #898781;
  --grid: #e1e0d9;
  --baseline: #c3c2b7;
  --hit: rgba(0, 0, 0, 0.06);
  --hit2: rgba(0, 0, 0, 0.11);
  /* Mark fill opacity is a token: the dark hues were validated as the composited
     result, not as the declared hex, so the two move together or not at all. */
  --g-rap: #256abf;
  --g-pop: #c33f6c;
  --g-rock: #006b00;
  --g-indie: #a86f00;
  --g-other: #6b6960;
  --mark-op: 0.8;
  --chip-h: 28px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --plane: #0d0d0d;
    --surface: #1a1a19;
    --panel: #141413;
    --ink: #ffffff;
    --ink2: #c3c2b7;
    --muted: #898781;
    --grid: #2c2c2a;
    --baseline: #383835;
    --hit: rgba(255, 255, 255, 0.07);
    --hit2: rgba(255, 255, 255, 0.14);
    --g-rap: #4a93ea;
    --g-pop: #e8659a;
    --g-rock: #1ec46a;
    --g-indie: #c98500;
    --g-other: #9d9b93;
    --mark-op: 0.85;
  }
}

:root[data-theme="dark"] {
  --plane: #0d0d0d;
  --surface: #1a1a19;
  --panel: #141413;
  --ink: #ffffff;
  --ink2: #c3c2b7;
  --muted: #898781;
  --grid: #2c2c2a;
  --baseline: #383835;
  --hit: rgba(255, 255, 255, 0.07);
  --hit2: rgba(255, 255, 255, 0.14);
  --g-rap: #4a93ea;
  --g-pop: #e8659a;
  --g-rock: #1ec46a;
  --g-indie: #c98500;
  --g-other: #9d9b93;
  --mark-op: 0.85;
}

:root[data-theme="light"] {
  --plane: #f9f9f7;
  --surface: #fcfcfb;
  --panel: #ffffff;
  --ink: #0b0b0b;
  --ink2: #52514e;
  --muted: #898781;
  --grid: #e1e0d9;
  --baseline: #c3c2b7;
  --hit: rgba(0, 0, 0, 0.06);
  --hit2: rgba(0, 0, 0, 0.11);
  --g-rap: #256abf;
  --g-pop: #c33f6c;
  --g-rock: #006b00;
  --g-indie: #a86f00;
  --g-other: #6b6960;
  --mark-op: 0.8;
}

* { box-sizing: border-box; margin: 0; padding: 0; border: 0; border-radius: 0; box-shadow: none; }

html, body { height: 100%; }

body {
  background: var(--plane);
  color: var(--ink);
  font: 14px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif;
  overflow: hidden;
  -webkit-text-size-adjust: 100%;
}

button, input { font: inherit; color: inherit; background: none; }
button { cursor: pointer; }
::selection { background: var(--hit2); }

/* ---------- loading plate ---------- */
.plate {
  position: fixed; inset: 0; z-index: 90;
  background: var(--plane);
  display: grid; place-items: center;
  transition: opacity .25s linear;
}
.plate[hidden] { display: none; }
.plate.gone { opacity: 0; pointer-events: none; }
.plate-in { width: 300px; text-align: left; }
.plate-title { font-size: 17px; font-weight: 600; }
.plate-sub { font-size: 13px; color: var(--muted); margin-top: 6px; }
.plate-bar { height: 2px; background: var(--grid); margin-top: 14px; overflow: hidden; }
.plate-bar i { display: block; height: 2px; width: 34%; background: var(--ink2); animation: slide 1.1s linear infinite; }
@keyframes slide { from { transform: translateX(-100%); } to { transform: translateX(320%); } }

/* ---------- top bar ---------- */
.topbar {
  height: 56px;
  display: flex; align-items: center; gap: 8px;
  padding: 0 10px;
  background: var(--panel);
}
/* The title is never truncated. When the switch groups and the figures line stop
   fitting beside it, the bar takes a second row instead. */
.h1 { font-size: 17px; font-weight: 600; letter-spacing: -0.01em; white-space: nowrap; flex: none; }
/* The readout for the current selection: figures in primary ink, the words after
   them muted, so the line reads as a stat line and not as a caption. */
.figures {
  font-size: 16px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding-left: 0; flex: 0 0 auto;
}
.figures b { color: var(--ink); font-weight: 500; font-variant-numeric: tabular-nums; }
/* 18px between the three switch groups; 28px before the about button and the
   theme toggle, which are not part of the switch row. Gap and plate tone do the
   separating, so each group still reads as one control. */
.switches { margin-left: auto; display: flex; align-items: center; gap: 18px; flex: none; }
.switches > .pill, .switches > .swgroup:last-child { margin-left: 10px; }
.swgroup { display: flex; align-items: center; gap: 6px; position: relative; }
/* Group captions only exist in the mobile sheet; on desktop the option labels
   carry the meaning and the width goes to the figures line. */
.swlabel { display: none; font-size: 13px; color: var(--muted); }
.sheet-head { display: none; }

/* The platform hint hangs directly under its own switch; absolute so it never
   pushes the single-row top bar wider. */
.swnote {
  position: absolute; top: 30px; right: 0;
  font-size: 13px; color: var(--muted); white-space: nowrap;
}
.swnote:empty { display: none; }
.switches.noted { margin-bottom: 20px; }

.seg { display: flex; height: 28px; background: var(--hit); }
.seg button {
  height: 28px; padding: 0 7px;
  font-size: 13px; line-height: 28px; color: var(--muted);
  white-space: nowrap;
}
.seg button:hover { color: var(--ink2); }
.seg button[aria-pressed="true"] { background: var(--hit2); color: var(--ink); font-weight: 500; }
.seg.disabled { opacity: .42; }
.seg.disabled button { cursor: not-allowed; }

.pill { height: 28px; padding: 0 10px; background: var(--hit); font-size: 13px; color: var(--ink2); white-space: nowrap; }
.pill:hover { background: var(--hit2); color: var(--ink); }

.theme-btn { width: 28px; height: 28px; display: grid; place-items: center; color: var(--muted); background: var(--hit); }
.theme-btn:hover { color: var(--ink); }
.theme-btn .i-sun { display: none; }
.theme-btn .i-moon { display: block; }
:root[data-theme="dark"] .theme-btn .i-sun { display: block; }
:root[data-theme="dark"] .theme-btn .i-moon { display: none; }
.mob-btn { display: none; }

/* ---------- shell: fixed rail + stage of plot over table ---------- */
.shell {
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: 1px;
  height: calc(100vh - 56px);
  background: var(--plane);
}
.stage { display: grid; grid-template-rows: 70fr 30fr; gap: 1px; min-width: 0; min-height: 0; }
.rail, .plotzone, .tablezone { background: var(--panel); min-width: 0; min-height: 0; }
.plotzone { background: var(--surface); position: relative; display: flex; flex-direction: column; }

/* ---------- rail ---------- */
.rail { display: flex; flex-direction: column; overflow: hidden; }
.rail-in { flex: 1; min-height: 0; overflow: hidden; padding: 12px; display: flex; flex-direction: column; gap: 12px; }
.rblock.grow { flex: 0 0 auto; }
.apply { display: none; }

.rblock { display: flex; flex-direction: column; gap: 7px; }
.rhead { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.rtitle { font-size: 13px; font-weight: 600; color: var(--ink2); text-transform: uppercase; letter-spacing: .06em; }
.rval, .rnote { font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

.search { width: 100%; height: 36px; padding: 0 54px 0 10px; background: var(--hit); color: var(--ink); font-size: 14px; }
.search::placeholder { color: var(--muted); }
.search:focus { outline: 2px solid var(--baseline); outline-offset: -2px; }
.search::-webkit-search-cancel-button { filter: grayscale(1); }

/* genre chips: legend and filter in one control */
.chips { display: flex; flex-wrap: wrap; gap: 4px; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: var(--chip-h); padding: 0 8px;
  background: var(--hit); font-size: 13px; color: var(--ink2); white-space: nowrap;
}
.chip:hover { background: var(--hit2); }
.chip i { width: 9px; height: 9px; flex: none; background: var(--sw, var(--muted)); }
.chip b { font-weight: 400; }
.chip u { text-decoration: none; color: var(--muted); font-variant-numeric: tabular-nums; }
.chip[aria-pressed="true"] { background: var(--hit2); color: var(--ink); }
.chip[aria-pressed="true"] b { font-weight: 600; }
.chip:disabled { opacity: .38; cursor: default; }
.chip:disabled:hover { background: var(--hit); }
.chip-all { padding: 0 10px; }
.chip-all i { display: none; }

/* search combobox: the field, its clear affordance and the suggestion list.
   The list separates from the rail by tone, on the page plane, not by a border. */
.combo { position: relative; }
.clear {
  position: absolute; right: 26px; top: 0; width: 26px; height: 36px;
  color: var(--muted); font-size: 17px; line-height: 36px;
}
.clear:hover { color: var(--ink); }
.clear[hidden] { display: none; }

/* The chevron is what says the list is there at all; without it the control
   reads as a plain text box and the browse affordance is invisible. */
.chev {
  position: absolute; right: 0; top: 0; width: 26px; height: 36px;
  color: var(--muted); font-size: 13px; line-height: 36px;
}
.chev:hover { color: var(--ink); }

.suggest {
  position: absolute; left: 0; right: 0; top: 37px; z-index: 50;
  max-height: 330px; overflow-y: auto;
  background: var(--plane); list-style: none;
}
.suggest[hidden] { display: none; }
.sug-head {
  padding: 7px 10px 3px;
  font-size: 13px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em;
}
.sug {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  min-height: 30px; padding: 5px 10px;
  font-size: 14px; color: var(--ink2); cursor: pointer;
}
.sug b { font-weight: 400; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sug u { text-decoration: none; color: var(--muted); font-size: 13px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sug:hover, .sug.on { background: var(--hit2); }
.sug-more { padding: 4px 10px 8px; font-size: 13px; color: var(--muted); }

/* two-handle range with a histogram strip behind it */
.range { position: relative; height: 40px; flex: none; }
.hist { position: absolute; left: 0; right: 0; top: 0; height: 24px; display: flex; align-items: flex-end; gap: 1px; }
.hist i { flex: 1; min-width: 1px; background: var(--grid); height: var(--h, 2px); min-height: 1px; }
.hist i.on { background: var(--baseline); }
.track { position: absolute; left: 0; right: 0; top: 29px; height: 3px; background: var(--grid); }
.fill { position: absolute; top: 0; height: 3px; background: var(--baseline); left: 0; right: 0; }
.range input[type="range"] {
  position: absolute; left: -3px; top: 16px; width: calc(100% + 6px); height: 28px;
  -webkit-appearance: none; appearance: none; background: none; pointer-events: none; margin: 0;
}
.range input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 6px; height: 17px; background: var(--ink); pointer-events: auto; cursor: ew-resize;
  border: 0; border-radius: 0;
}
.range input[type="range"]::-moz-range-thumb {
  width: 6px; height: 17px; background: var(--ink); pointer-events: auto; cursor: ew-resize;
  border: 0; border-radius: 0;
}
.range input[type="range"]:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--baseline); }

.chip.hide { display: none; }
.more { font-size: 13px; color: var(--muted); text-align: left; padding: 5px 6px; }
.more:hover { color: var(--ink); }

.rail-foot { margin-top: auto; padding-top: 6px; display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 13px; color: var(--muted); flex: none; }
.reset { font-size: 13px; color: var(--ink2); text-decoration: underline; text-underline-offset: 3px; }
.reset:hover { color: var(--ink); }

/* ---------- plot ---------- */
.plotwrap { flex: 1; min-height: 0; }
#plot { width: 100%; height: 100%; }
.js-plotly-plot .plotly .modebar { display: none !important; }

/* Annotations cannot tween, so they fade across a metric change; an axis type
   change cannot be interpolated at all, so the plot area cross-fades. */
#plot .textpoint { transition: opacity 150ms linear; }
#plot.labels-out .textpoint { opacity: 0; }

/* Labels carry the surface colour behind their glyphs, painted under the fill so
   it separates them from the marks without outlining them. Invisible where there
   is nothing behind. */
#plot .textpoint text {
  paint-order: stroke;
  stroke: var(--surface);
  stroke-width: 3px;
  stroke-linejoin: round;
}

@media (prefers-reduced-motion: reduce) {
  #plot, #plot .textpoint, .tbl tbody tr, .plate { transition: none !important; }
  .ov-panel { animation: none; }
  .plate-bar i { animation: none; }
}

/* Clears the year and plays ranges a drawn box writes into, and nothing else.
   Sits on the plot rather than in the rail so it is next to what it undoes. */
.selreset {
  position: absolute; left: 16px; top: 16px; z-index: 30;
  height: 28px; padding: 0 10px;
  background: var(--panel); color: var(--ink2); font-size: 13px;
}
.selreset:hover { color: var(--ink); }
.selreset[hidden] { display: none; }

.tip {
  position: absolute; z-index: 40; width: 268px; padding: 9px 10px;
  background: var(--panel); color: var(--ink); font-size: 13px; line-height: 1.4;
  outline: 1px solid var(--grid); pointer-events: none;
}
.tip[hidden] { display: none; }
.tip-top { display: flex; gap: 9px; }
.tip img { width: 46px; height: 46px; flex: none; object-fit: cover; background: var(--hit); }
.tip-t { font-weight: 600; }
.tip-a { color: var(--ink2); }
.tip-rows { margin-top: 7px; display: flex; flex-direction: column; gap: 2px; }
.tip-rows div { display: flex; justify-content: space-between; gap: 10px; color: var(--ink2); }
.tip-rows b { font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
.tip-tag { margin-top: 7px; display: flex; align-items: center; gap: 6px; color: var(--muted); }
.tip-tag i { width: 8px; height: 8px; background: var(--sw); flex: none; }
.tip-warn { margin-top: 6px; color: var(--g-indie); }

/* ---------- table ---------- */
.tablezone { display: flex; flex-direction: column; }
.twrap { flex: 1; min-height: 0; overflow: auto; }
.tbl { width: 100%; border-collapse: collapse; table-layout: fixed; }
.tbl th {
  position: sticky; top: 0; z-index: 2;
  height: 30px; padding: 0 8px;
  background: var(--panel); color: var(--ink2);
  font-size: 13px; font-weight: 600; text-align: left; white-space: nowrap;
  cursor: pointer; user-select: none;
}
.tbl th::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--grid); }
.tbl th button { width: 100%; height: 30px; font: inherit; color: inherit; text-align: inherit; white-space: nowrap; }
.tbl th.num button { text-align: right; }
.tbl th:hover { color: var(--ink); }
.tbl th[aria-sort] { color: var(--ink); }
.tbl th .dir { color: var(--muted); font-weight: 400; }
.tbl th.num, .tbl td.num { text-align: right; font-variant-numeric: tabular-nums; }
.tbl th.bar { width: 4px; padding: 0; cursor: default; }
.tbl td {
  height: 32px; padding: 0 8px;
  font-size: 14px; color: var(--ink2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tbl td.bar { padding: 0; background: var(--sw, var(--muted)); }
.tbl td.title { color: var(--ink); }
.tbl td.dim { color: var(--muted); font-size: 13px; }
.tbl tbody tr { cursor: pointer; }
.tbl tbody tr:hover td { background: var(--hit); }
.tbl tbody tr:hover td.bar { background: var(--sw, var(--muted)); }
.tbl tbody tr.on td { background: var(--hit2); }
.tbl tbody tr.on td.bar { background: var(--sw, var(--muted)); }
.tbl mark { background: none; color: var(--ink); font-weight: 700; }
.tempty { padding: 14px 10px; font-size: 13px; color: var(--muted); }
.tfoot {
  flex: none; height: 26px; padding: 0 10px;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font-size: 13px; color: var(--muted);
}
.tfoot button { font-size: 13px; color: var(--ink2); text-decoration: underline; text-underline-offset: 3px; }
.tfoot button:hover { color: var(--ink); }
.tfoot button[hidden] { display: none; }

/* ---------- about overlay ---------- */
.overlay { position: fixed; inset: 0; z-index: 70; }
.overlay[hidden] { display: none; }
.ov-back { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.5); }
.ov-panel {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(560px, 100%);
  background: var(--plane);
  display: flex; flex-direction: column;
  animation: slidein .18s ease-out;
}
@keyframes slidein { from { transform: translateX(24px); opacity: .4; } to { transform: none; opacity: 1; } }
.ov-head { flex: none; height: 56px; padding: 0 18px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ov-head h2 { font-size: 17px; font-weight: 600; }
.ov-head button { height: 30px; padding: 0 10px; background: var(--hit); font-size: 13px; color: var(--ink2); }
.ov-head button:hover { background: var(--hit2); color: var(--ink); }
.ov-body { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 18px 28px; }
.ov-body p { font-size: 14px; color: var(--ink2); margin-bottom: 14px; max-width: 62ch; }
.ov-body b { color: var(--ink); font-weight: 600; }
.ov-body a { color: var(--ink); }
/* The credit is not part of the methodology, so it sits away from it. */
.ov-credit { margin-top: 26px; color: var(--muted); }

/* ---------- mobile ---------- */
@media (max-width: 999px) {
  .topbar { height: 52px; padding: 0 12px; gap: 10px; }
  .h1 { font-size: 15px; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
  /* The figures line moves into the sheet rather than wrapping the top bar. */
  .figures { white-space: normal; padding: 0; }
  .mob-btn { display: block; margin-left: auto; min-height: 44px; padding: 0 12px; background: var(--hit); font-size: 15px; flex: none; }

  .shell { grid-template-columns: 1fr; grid-template-rows: 1fr; height: calc(100vh - 52px); gap: 0; }
  /* Half the screen to the chart, the rest to the table, which is what scrolls. */
  .stage { grid-area: 1 / 1; grid-template-rows: 50vh 1fr; gap: 1px; }

  /* the rail becomes the one sheet, carrying the switches with it */
  .rail {
    position: fixed; inset: 0; z-index: 60;
    display: none;
  }
  body[data-sheet="filters"] .rail { display: flex; }
  body[data-sheet="filters"] { overflow: hidden; }
  .rail-in { overflow-y: auto; padding: 12px 12px 0; gap: 16px; }

  .sheet-head { display: flex; align-items: center; height: 32px; font-size: 17px; font-weight: 600; }
  #sheet-switches { display: flex; flex-direction: column; gap: 16px; }
  .switches { margin: 0; flex-direction: column; align-items: stretch; gap: 16px; width: 100%; }
  .switches > .pill, .switches > .swgroup:last-child { margin-left: 0; }
  .switches.noted { margin-bottom: 0; }
  .swgroup { flex-direction: column; align-items: stretch; gap: 6px; }
  .swlabel { display: block; font-size: 13px; }
  .switches .seg { height: 44px; }
  .switches .seg button { height: 44px; line-height: 44px; flex: 1; text-align: center; font-size: 15px; }
  .swnote { position: static; white-space: normal; font-size: 13px; }
  .pill, .theme-btn { width: 100%; height: 44px; font-size: 15px; }

  .search { height: 44px; }
  .clear { height: 44px; width: 44px; right: 44px; }
  .chev { height: 44px; width: 44px; }
  .sug { min-height: 44px; }
  .chip { height: 44px; padding: 0 12px; font-size: 14px; }
  .chip i { width: 11px; height: 11px; }
  .more { min-height: 44px; padding: 10px 6px; font-size: 14px; }
  .reset { min-height: 44px; display: inline-flex; align-items: center; font-size: 14px; }
  .rail-foot { min-height: 44px; }
  .range { height: 52px; }
  .hist { height: 28px; }
  .track { top: 36px; }
  .range input[type="range"] { top: 14px; height: 44px; }
  .range input[type="range"]::-webkit-slider-thumb { width: 6px; height: 22px; }
  .range input[type="range"]::-moz-range-thumb { width: 6px; height: 22px; }
  .apply { display: block; height: 52px; flex: none; background: var(--hit2); color: var(--ink); font-size: 15px; font-weight: 600; }

  .tbl td { height: 44px; }
  /* Album, artist and the one figure the plot is drawing. Genre survives as the
     colour bar at the row's left edge, and the rest give up their width. */
  .tbl .tag,
  .tbl th[data-key="year"], .tbl td.year { display: none; }
  .tbl[data-fig="adj"] th[data-key="streams"], .tbl[data-fig="adj"] td.metric { display: none; }
  .tbl[data-fig="metric"] th[data-key="adjusted"], .tbl[data-fig="metric"] td.adj { display: none; }
  /* Desktop widths are inline on the th, so the narrow layout has to outrank them
     to give the width the dropped columns freed back to the two name columns. */
  .tbl th.title { width: 38% !important; }
  .tbl th[data-key="artist"] { width: 31% !important; }
  .tbl th[data-key="streams"], .tbl th[data-key="adjusted"] { width: 31% !important; }
  .tip { width: min(88vw, 268px); }
  .ov-panel { width: 100%; }
}

/* One row needs about 1592px to hold a whole title, the 16px readout and the
   grouped switches. Below that the bar takes a second row rather than truncating
   the title or dropping the readout: h1 and figures above, switches below. */
@media (max-width: 1599px) and (min-width: 1000px) {
  .topbar { height: 70px; flex-wrap: wrap; align-content: center; row-gap: 5px; }
  .switches { width: 100%; margin-left: 0; }
  .shell { height: calc(100vh - 70px); }
}
