/* Palette follows Last.fm's own dark theme: near-black greys with their
   signature red (#d51007) as the single accent. Deliberately not a clone --
   this is a third-party tool and should not pass itself off as Last.fm -- but
   it should feel like it belongs next to it. */
:root{
  --bg:#131313; --panel:#1c1c1c; --line:#2b2b2b; --ink:#f0f0f0; --dim:#999;
  --accent:#d51007;          /* Last.fm red */
  --accent-hover:#ff2010;
  --good:#5cb85c; --warn:#e0af68; --bad:#d51007; --era:#b56bd8;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
  font:15px/1.55 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,
    "Helvetica Neue",Arial,sans-serif;
  -webkit-font-smoothing:antialiased}
a{color:var(--ink);text-decoration:none;border-bottom:1px solid var(--line)}
a:hover{color:var(--accent);border-bottom-color:var(--accent)}
code{font:12.5px ui-monospace,SFMono-Regular,Menlo,monospace}
.wrap{max-width:1080px;margin:0 auto;padding:40px 20px 40px}
h1{font-size:32px;margin:0 0 6px;letter-spacing:-.02em;font-weight:800}
h1 .dot{color:var(--accent)}
/* The lockup ships as a dark-theme PNG: the original artwork has black ink for
   light backgrounds, which would be invisible here. width/height are on the
   img so the header does not reflow while it loads. */
h1.brand{margin:0 0 14px;line-height:0}
h1.brand img{width:270px;max-width:72vw;height:auto;display:block}
@media (max-width:520px){ h1.brand img{width:210px} }
h2{font-size:13px;text-transform:uppercase;letter-spacing:.1em;color:var(--dim);
  margin:38px 0 12px;font-weight:700;
  border-bottom:1px solid var(--line);padding-bottom:8px}
/* Full content width, matching the panels below it, rather than a narrow
   measure. */
.lede{color:var(--dim);margin:0 0 26px}
.sub{color:var(--dim);font-size:13px;margin-bottom:26px}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:18px}
.grid{display:grid;gap:14px}
.g4{grid-template-columns:repeat(auto-fit,minmax(170px,1fr))}
.g2{grid-template-columns:repeat(auto-fit,minmax(320px,1fr))}
form{display:flex;gap:9px;flex-wrap:wrap;align-items:center}
input,select,button{background:#0d0d0d;color:var(--ink);border:1px solid var(--line);
  border-radius:4px;padding:11px 13px;font:inherit;font-size:14px}
input{min-width:230px}
input:focus,select:focus{outline:none;border-color:var(--accent)}
button{background:var(--accent);color:#fff;border-color:transparent;
  font-weight:700;cursor:pointer;text-transform:uppercase;
  letter-spacing:.04em;font-size:13px;border-radius:4px}
button:hover:not(:disabled){background:var(--accent-hover)}
button:disabled{opacity:.45;cursor:default}
button.ghost{background:transparent;color:var(--dim);border-color:var(--line);
  font-weight:600}
button.ghost:hover{background:transparent;color:var(--ink);
  border-color:var(--dim)}
.bar{height:7px;background:var(--line);border-radius:99px;overflow:hidden;margin-top:9px}
.bar>i{display:block;height:100%;background:var(--accent);transition:width .25s}
.metric .n{font-size:29px;font-weight:650;letter-spacing:-.02em}
.metric .l{color:var(--dim);font-size:12px;text-transform:uppercase;
  letter-spacing:.06em;margin-top:2px}
.score{font-size:52px;font-weight:700;line-height:1}
.row{display:flex;justify-content:space-between;gap:12px;padding:5px 0;
  border-bottom:1px solid var(--line);font-size:13px}
.row:last-child{border-bottom:0}
.row .v{color:var(--dim);white-space:nowrap;font-variant-numeric:tabular-nums}
.tag{display:inline-block;font-size:11px;padding:1px 7px;border-radius:99px;
  border:1px solid var(--line);color:var(--dim);margin:0 6px 4px 0}
.t-error{color:var(--bad);border-color:var(--bad)}
.t-split{color:var(--warn);border-color:var(--warn)}
.t-review{color:var(--accent);border-color:var(--accent)}
.t-anomaly{color:var(--era);border-color:var(--era)}
.t-unfixable{color:var(--dim)}
.issue{border:1px solid var(--line);border-radius:9px;padding:13px 15px;
  margin-bottom:9px;background:var(--panel)}
.issue h3{margin:0 0 7px;font-size:14.5px;font-weight:600}
.issue p{margin:6px 0 0;color:var(--dim);font-size:13px}
.issue .mem{margin-top:9px;font-size:12px;color:var(--dim);
  font-variant-numeric:tabular-nums}
.issue .mem code{background:#0c0e12;padding:1px 5px;border-radius:4px;color:var(--ink)}
.issue .links{margin-top:7px;padding-top:7px;border-top:1px solid var(--line)}
.issue .links a{color:var(--accent);border-bottom:0;font-size:12px}
.issue .links a:hover{border-bottom:1px solid var(--accent)}
.mv{display:flex;gap:9px;font-size:13px;padding:5px 0;border-bottom:1px solid var(--line)}
.up{color:var(--good)} .down{color:var(--bad)}
.err{border-color:var(--bad);color:var(--bad)}
.hide{display:none}
.note{font-size:12.5px;color:var(--dim);margin-top:9px}
.nav{display:flex;gap:18px;font-size:13px;margin-bottom:24px}
.setup{background:#0d0d0d;border-radius:4px;padding:12px 14px;margin-top:12px;
  overflow-x:auto}
.setup pre{margin:0;font:12.5px ui-monospace,Menlo,monospace;color:var(--ink)}

/* ---- Last.fm attribution ------------------------------------------------
   The API terms ask for attribution and a link back, so this is a compliance
   item as much as a design one. Rendered as type rather than a copy of their
   logo: reproducing a trademark imprecisely is worse than not using it, and a
   hotlinked hashed asset from their CDN breaks the moment they redeploy. */
.attrib{border-top:1px solid var(--line);margin-top:56px;padding:22px 0 8px;
  display:flex;flex-wrap:wrap;gap:8px 18px;align-items:center;
  font-size:12.5px;color:var(--dim)}
.attrib a{border-bottom:0}
.lfm{display:inline-flex;align-items:center;gap:7px;font-weight:800;
  letter-spacing:-.02em;color:var(--ink);font-size:14px;line-height:1}
.lfm:hover{color:var(--ink);opacity:.85}

/* The official asset is the 1200x630 social share image, so the red wordmark
   sits inside dark padding. Shown whole rather than cropped: a crop needs
   hard-coded pixel offsets into someone else's file, which silently breaks if
   they ever re-export it. The padding is near-black and blends into --bg, so
   there is nothing to gain from the fragile version. */
.lfm-logo{display:block;width:96px;height:auto;line-height:0}
.lfm-logo img{display:block;width:100%;height:auto;border-radius:2px}

/* Shown only if the hotlinked asset 404s. Last.fm fingerprints static files
   (lastfm_logo_facebook.15d8133be114.png), so that URL dies whenever they
   redeploy. Self-hosting the file in docs/ removes this failure mode. */
.lfm-fallback{display:none;align-items:center;gap:7px}
.lfm-fallback .mark{display:inline-flex;align-items:center;justify-content:center;
  width:22px;height:22px;border-radius:3px;background:var(--accent);
  color:#fff;font-size:10px;font-weight:800;letter-spacing:-.04em}
.attrib .sep{color:var(--line)}

/* ------------------------------------------------------------------ tooltips */
/* A real <button>, not a <span title="...">, for three reasons: native titles
   are unstyleable and slow to appear, they never show on touch devices at all,
   and a button is keyboard-focusable so the content is reachable without a
   mouse. The tip lives in a data attribute rather than a hidden element so the
   markup stays a single tag. */
/* Longhand, not the `font` shorthand. `font:600 10px/1 inherit` looks
   reasonable and is INVALID: `inherit` is not a legal font-family inside the
   shorthand, so the browser drops the entire declaration. The icon then got no
   sizing at all and the tooltip inherited bold text from whatever it sat next
   to. Longhand cannot fail this way. */
.i{display:inline-flex;align-items:center;justify-content:center;
  width:15px;height:15px;padding:0;margin:0 3px 0 5px;border-radius:99px;
  background:transparent;border:1px solid var(--dim);color:var(--dim);
  font-family:inherit;font-size:10px;font-weight:600;line-height:1;
  text-transform:none;letter-spacing:0;
  cursor:help;vertical-align:middle;position:relative;flex:0 0 auto}
.i:hover,.i:focus-visible{background:var(--dim);color:var(--panel);
  border-color:var(--dim);outline:none}

/* Positioned on a wrapper so the tip is not clipped by the button's own tiny
   box, and so transform-centring is relative to the icon. */
.i::after{content:attr(data-tip);position:absolute;left:50%;bottom:calc(100% + 8px);
  transform:translateX(-50%);width:max-content;max-width:min(300px,72vw);
  background:#0a0a0a;color:var(--ink);border:1px solid var(--line);
  border-radius:7px;padding:10px 12px;
  font-family:inherit;font-size:12.5px;font-weight:400;line-height:1.55;
  text-transform:none;letter-spacing:0;text-align:left;white-space:normal;
  opacity:0;visibility:hidden;transition:opacity .12s;z-index:50;
  box-shadow:0 8px 24px rgba(0,0,0,.55);pointer-events:none}
.i::before{content:"";position:absolute;left:50%;bottom:calc(100% + 3px);
  transform:translateX(-50%);border:5px solid transparent;
  border-top-color:var(--line);opacity:0;visibility:hidden;
  transition:opacity .12s;z-index:51;pointer-events:none}
.i:hover::after,.i:hover::before,
.i:focus-visible::after,.i:focus-visible::before{opacity:1;visibility:visible}

/* Near the left or right edge the centred tip would overflow the viewport.
   Nudged rather than flipped: a flip needs measurement in JS, and these tips
   are short enough that a fixed nudge is sufficient and cannot jitter. */
.i.tip-l::after{left:0;transform:none}
.i.tip-r::after{left:auto;right:0;transform:none}

/* Below-the-icon variant, for tips on the top row where there is no room above. */
.i.tip-below::after{bottom:auto;top:calc(100% + 8px)}
.i.tip-below::before{bottom:auto;top:calc(100% + 3px);
  border-top-color:transparent;border-bottom-color:var(--line)}

/* Compact fact row, replacing three stacked paragraphs of disclosure copy. */
.facts{display:flex;flex-wrap:wrap;gap:8px 22px;align-items:center}
/* baseline, not center: the icon is vertically centred against the text by its
   own vertical-align, and flex centring fought that and shifted it up a pixel. */
.facts>span{display:inline-flex;align-items:baseline;flex-wrap:wrap}
.facts a{font-size:12.5px;margin-left:6px}
.facts #usage{margin-left:0}

/* --------------------------------------------------------- jump to issues */
.jump{display:inline-flex;align-items:center;gap:8px;margin-top:14px;
  background:transparent;border:1px solid var(--accent);color:var(--accent);
  border-radius:99px;padding:9px 17px;font-weight:700;font-size:12.5px;
  cursor:pointer;text-transform:uppercase;letter-spacing:.04em}
.jump:hover{background:var(--accent);color:#fff;border-color:var(--accent)}
.jump .arr{font-size:15px;line-height:1;transform:translateY(1px)}

/* Respects a reduced-motion preference: smooth scrolling is set here rather
   than inline so the media query can switch it off, which an inline
   behavior:"smooth" in JS could not. */
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .i::after,.i::before{transition:none}
}

/* Anchor offset so the heading is not flush against the viewport top after a
   jump. */
#issues-anchor{scroll-margin-top:22px}

/* ----------------------------------------------------- scan summary panel */
/* Shown after a run finishes. A scan can end four ways that used to look
   identical: complete, complete-but-Spotify-died, stopped, or lookups skipped.
   This is where the difference gets stated. */
.done{margin-top:16px;border-color:#2f4030}
.done .note:last-child{margin-bottom:0}
.done-head{display:flex;align-items:center;gap:9px;flex-wrap:wrap;
  margin-bottom:8px;font-size:14px}
.done-head .v{margin-left:auto;font-size:12.5px;
  font-variant-numeric:tabular-nums}
.done-mark{display:inline-flex;align-items:center;justify-content:center;
  width:19px;height:19px;border-radius:99px;font-size:11px;font-weight:800;
  flex:0 0 auto}
.done-mark.ok{background:var(--good);color:#0d1a0d}
.done-mark.part{background:var(--warn);color:#221a08}
