/* nine71 charts
   ---------------------------------------------------------------------------
   Same identity as nine71.ai: the tokens, the typeface and the component
   language are lifted from site/styles.css so this reads as the same company
   rather than as a second brand. If a value here disagrees with that file, that
   file is right.

   The things that carry the identity, and that are easy to lose:
     - Outfit, and only Outfit. Numbers are Outfit with tabular figures, not a
       mono. A second family is the fastest way to stop looking like nine71.
     - Light weights. Display type is 300/400 with negative tracking. Nothing
       here is 700 - a bold grotesk headline is not what this brand sounds like.
     - Rounded. 18px on lists and cards, 100px on bars and pills, 6px on buttons.
     - Violet is the crowd, gold is uncertain. Gold carries the same meaning it
       carries on the marketing site's chart, where .tag-tent marks a tentative
       ID. An unidentified track here is the same idea, so it takes the same
       colour rather than a new one. */

:root {
    --bg: #111212;
    --surface: #191919;
    --surface-2: #202020;
    --border: #313131;
    --border-2: #3d3d3d;
    --nav-border: #2a2a2a;
    --brand: #7065f0;
    --brand-light: #9fa3fc;
    --brand-lighter: #caccfd;
    --gold: #d8a13a;
    --gold-soft: rgba(216, 161, 58, 0.14);
    --text: #E8E8E8;
    --text-2: #A0A0A0;
    --text-3: #767676;
    --font: 'Outfit', -apple-system, BlinkMacSystemFont, sans-serif;
    --container: 1200px;
    --pad: 2rem;
}

* { margin: 0; padding: 0; box-sizing: border-box; }
/* The hidden attribute wins over any display rule. Without this a block that
   sets display:flex (the submit page's sign-in notice) stays visible with
   hidden set - which is how a signed-in reader was told to sign in. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; }

body {
    font-family: var(--font);
    background: var(--bg);
    color: var(--text);
    line-height: 1.6;
    font-weight: 300;
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}

a { color: inherit; text-decoration: none; }
ol, ul { list-style: none; }

:focus-visible { outline: 2px solid var(--brand-light); outline-offset: 3px; }

.container { max-width: var(--container); margin: 0 auto; padding: 0 var(--pad); }

.vh {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ---------- nav ---------- */
/* The masthead from the marketing site: links left, wordmark centred, one CTA
   right. Centring the mark is what makes it read as a masthead rather than as
   app chrome, and it is the single most recognisable piece of the identity. */

.nav {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 100;
    border-bottom: 1px solid transparent;
    transition: background 200ms ease, border-color 200ms ease;
}
.nav.scrolled {
    background: rgba(17, 18, 18, 0.78);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border-bottom-color: var(--border);
}
.nav-inner {
    max-width: var(--container);
    margin: 0 auto;
    padding: 1.4rem var(--pad);
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 1.5rem;
}
.nav-links {
    grid-column: 1;
    grid-row: 1;
    display: flex;
    gap: 1.9rem;
    justify-self: start;
}
.nav-links a {
    color: var(--text-2);
    font-size: 0.95rem;
    font-weight: 400;
    transition: color 140ms ease;
}
.nav-links a:hover, .nav-links a.on { color: var(--text); }

.nav-logo {
    grid-column: 2;
    justify-self: center;
    display: inline-flex;
    align-items: baseline;
    gap: 0.55rem;
    /* Sized like nine71.ai: font-size on the link, the mark in em off it, so
       the two scale together and the label sits on the wordmark's baseline. */
    font-size: 1.2rem;
    line-height: 1;
    color: #fff;
    user-select: none;
}
.wordmark-img { display: block; height: 0.72em; width: auto; }
/* The module label beside the mark, the same device the product pages use. */
.nav-mod {
    line-height: 1;
    font-size: 0.95rem;
    font-weight: 300;
    color: var(--text-3);
    letter-spacing: -0.01em;
}

.nav-cta {
    grid-column: 3; justify-self: end;
    display: inline-flex; align-items: center; gap: 0.6rem;
}

.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    border: none;
    border-radius: 6px;
    padding: 0.8rem 1.5rem;
    font: 500 0.95rem var(--font);
    cursor: pointer;
    transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}
.btn-small { padding: 0.55rem 1.1rem; font-size: 0.88rem; }
.btn-light { background: #E8E8E8; color: #121212; }
.btn-light:hover { background: var(--brand-lighter); }
.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border-2); }
.btn-ghost:hover { border-color: var(--brand-light); color: var(--brand-lighter); }

/* ---------- page hero ---------- */

.page-hero {
    padding: 11rem var(--pad) 4rem;
    text-align: center;
    background: linear-gradient(180deg, #23252A 0%, #191A1D 42%, var(--bg) 100%);
}
.page-title {
    font-size: clamp(2.3rem, 4.4vw, 3.15rem);
    font-weight: 400;
    letter-spacing: -0.02em;
    line-height: 1.22;
    color: var(--text);
}
.page-sub {
    margin: 1.2rem auto 0;
    max-width: 42rem;
    color: var(--text-2);
    font-size: 1.125rem;
    line-height: 1.6;
}

.hero-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 2.6rem;
    justify-content: center;
    margin-top: 2.6rem;
    color: var(--text-3);
    font-size: 0.85rem;
}
.hero-stats b {
    display: block;
    color: var(--text);
    font-size: 1.5rem;
    font-weight: 300;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
}

/* ---------- sections ---------- */

.section { padding: 5rem 0 7.5rem; }
.section.container { padding-left: var(--pad); padding-right: var(--pad); }
/* .page-hero already carries 4rem of bottom padding, so a section that follows
   it directly does not need its own full top pad on top of that. */
.section-tight { padding-top: 1.5rem; }
.section-head { margin-bottom: 2.2rem; }
/* A head immediately followed by its lede owns the gap once, not twice. */
.section-head:has(+ .chart-lede) { margin-bottom: 0.5rem; }
.section-head h2 {
    font-size: 1.5rem;
    font-weight: 400;
    letter-spacing: -0.02em;
}
.chart-lede {
    margin: 0.6rem 0 2rem;
    max-width: 46rem;
    color: var(--text-2);
    font-size: 0.95rem;
}

/* ---------- controls ---------- */

.listing-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1.2rem;
    margin-bottom: 1.8rem;
}
.controls { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }

#q {
    width: 17rem;
    padding: 0.6rem 0.95rem;
    font: 300 0.95rem var(--font);
    color: var(--text);
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid var(--border);
    border-radius: 100px;
}
#q::placeholder { color: var(--text-3); }
#q:focus { outline: none; border-color: var(--brand); background: rgba(112, 101, 240, 0.06); }

/* Same pill row as .chart-tabs on the marketing site. */
.filter {
    padding: 0.5rem 1rem;
    font: 400 0.85rem var(--font);
    color: var(--text-3);
    background: transparent;
    border: 1px solid var(--border);
    border-radius: 100px;
    cursor: pointer;
    transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
}
.filter:hover { color: var(--text); border-color: var(--border-2); }
.filter.on { background: var(--text); border-color: var(--text); color: #121212; }

.filters { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.filter[hidden] { display: none; }

/* The rest of the genres, behind one button. Dashed so it reads as a control on
   the row rather than as one more genre sitting among the genres. */
.filter-menu-wrap { position: relative; display: inline-flex; }
.filter-more { border-style: dashed; color: var(--text-3); }
.filter-more::after {
    content: "";
    display: inline-block;
    width: 0.34em;
    height: 0.34em;
    margin-left: 0.5em;
    vertical-align: 0.15em;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(45deg);
    transition: transform 140ms ease;
}
.filter-more:hover { color: var(--text); }
.filter-more.open { border-style: solid; color: var(--text); }
.filter-more.open::after { transform: rotate(-135deg); }
/* A genre chosen from the menu is not on the row, so the button wears it. */
.filter-more.picked {
    border-style: solid;
    background: var(--text);
    border-color: var(--text);
    color: #121212;
}

/* Must come with the display rule below: `display: grid` outranks the browser's
   own [hidden] { display: none }, so without this the menu is never closed -
   which is why .card[hidden] and .filter[hidden] are spelled out too. */
.filter-menu[hidden] { display: none; }

.filter-menu {
    position: absolute;
    top: calc(100% + 0.45rem);
    left: 0;
    z-index: 40;
    display: grid;
    gap: 0.1rem;
    min-width: 13rem;
    /* Never wider than the phone it opens on, and never taller than the screen:
       twenty rows is longer than a short viewport. */
    max-width: min(78vw, 20rem);
    max-height: min(62vh, 26rem);
    overflow-y: auto;
    padding: 0.35rem;
    background: var(--surface);
    border: 1px solid var(--border-2);
    border-radius: 14px;
    box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55);
}
.filter-menu .filter {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    width: 100%;
    padding: 0.5rem 0.7rem;
    text-align: left;
    border: 0;
    border-radius: 9px;
}
.filter-menu .filter:hover { background: var(--surface-2); color: var(--text); }
.menu-count { color: var(--text-3); font-size: 0.78rem; font-variant-numeric: tabular-nums; }
.filter-menu .filter:hover .menu-count { color: var(--text-2); }
.filter-menu .filter.on .menu-count { color: rgba(18, 18, 18, 0.55); }

/* ---------- the bottom of the list ---------- */

.more {
    display: flex;
    justify-content: center;
    padding: 2rem 0 0.5rem;
}
.more[hidden] { display: none; }
.more-link {
    padding: 0.7rem 1.4rem;
    font-size: 0.9rem;
    color: var(--text-2);
    border: 1px solid var(--border);
    border-radius: 100px;
    transition: color 140ms ease, border-color 140ms ease;
}
.more-link:hover { color: var(--text); border-color: var(--border-2); }
.more-status {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--text-3);
    font-size: 0.9rem;
}
.more-status::before {
    content: "";
    width: 0.85rem;
    height: 0.85rem;
    border: 2px solid var(--border-2);
    border-top-color: var(--brand-light);
    border-radius: 50%;
    animation: spin 700ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
/* Someone who has asked for less motion gets the words without the wheel. */
@media (prefers-reduced-motion: reduce) {
    .more-status::before { animation: none; border-top-color: var(--border-2); }
}

.pager { display: flex; gap: 0.6rem; justify-content: space-between; padding-top: 2rem; }
.pager-link { color: var(--text-2); font-size: 0.95rem; }
.pager-link:hover { color: var(--text); }
.page-title-sm { font-size: clamp(1.8rem, 5vw, 2.4rem); }

/* ---------- set cards ---------- */

.cards { display: grid; gap: 1rem; }

.card {
    border: 1px solid var(--border);
    border-radius: 18px;
    background: rgba(255, 255, 255, 0.02);
    transition: background 140ms ease, border-color 140ms ease;
    /* The index renders every set, and every set draws a tick per track - a
       quarter of a million elements once the corpus passed a thousand sets,
       which a phone spends seconds laying out before it paints anything. This
       lets the browser skip the layout of cards that are not on screen. The
       size is a card's usual height, so the scrollbar still means something,
       and a browser without support just renders the list as it always did. */
    content-visibility: auto;
    contain-intrinsic-size: auto 236px;
}
.card:hover { background: var(--surface-2); border-color: var(--border-2); }
.card[hidden] { display: none; }
.card-link { display: block; padding: 1.5rem 1.6rem 1.2rem; }

.card-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1.5rem;
}
.card-artist {
    font-size: 1.35rem;
    font-weight: 400;
    letter-spacing: -0.022em;
    line-height: 1.2;
    color: var(--text);
}
.card-event { margin-top: 0.2rem; color: var(--text-2); font-size: 0.95rem; }
.card-date {
    flex: none;
    text-align: right;
    color: var(--text-3);
    font-size: 0.78rem;
    line-height: 1.45;
}
.card-date b {
    display: block;
    color: var(--text-2);
    font-weight: 400;
    font-size: 0.88rem;
    font-variant-numeric: tabular-nums;
}

.card-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.4rem;
    color: var(--text-3);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
}
.card-stat b { color: var(--text-2); font-weight: 400; }
.card-genres { margin-left: auto; }

.no-results { margin-top: 3rem; text-align: center; color: var(--text-3); }

/* ---------- the set ruler ---------- */
/* The one thing here the marketing site does not already have. Each cue sits at
   its real elapsed position, so a four-hour set looks like four hours, a fast
   b2b bunches up, and a long unidentified stretch reads as a gap rather than as
   one more row. Violet is identified, gold is not. */

.ruler { position: relative; }
.ruler-track {
    position: relative;
    height: 100%;
    background: linear-gradient(to right, rgba(255, 255, 255, 0.09) 0 100%) center / 100% 1px no-repeat;
}

.ruler-mini { height: 30px; margin: 1.1rem 0 0.9rem; }
.ruler-mini .tick {
    position: absolute;
    bottom: 50%;
    width: 2px;
    height: 13px;
    border-radius: 100px;
    background: var(--brand-light);
    opacity: 0.85;
}
.ruler-mini .tick-id { background: var(--gold); height: 7px; opacity: 0.8; }
.ruler-mini .hour { height: 16px; }
.ruler-mini .hour i { top: 16px; font-size: 0.6rem; }

.ruler-live { height: 62px; }
.ruler-live .ruler-track { background-position-y: 42px; }
.ruler-live .tick {
    position: absolute;
    bottom: 20px;
    width: 3px;
    height: 22px;
    padding: 0;
    background: var(--brand-light);
    border: none;
    border-radius: 100px;
    cursor: pointer;
    opacity: 0.8;
    transition: opacity 140ms ease, height 140ms ease, background 140ms ease;
}
/* A 3px target is not clickable; this is the hit area, not a visible shape. */
.ruler-live .tick::after { content: ""; position: absolute; inset: -14px -5px -18px; }
.ruler-live .tick:hover,
.ruler-live .tick:focus-visible { opacity: 1; height: 32px; }
.ruler-live .tick-id { background: var(--gold); height: 13px; opacity: 0.75; }
.ruler-live .tick-id:hover,
.ruler-live .tick-id:focus-visible { height: 22px; }
.ruler-live .tick.on { opacity: 1; height: 34px; background: var(--brand-lighter); }

.hour {
    position: absolute;
    bottom: 0;
    width: 1px;
    height: 22px;
    background: rgba(255, 255, 255, 0.09);
}
.hour i {
    position: absolute;
    top: 22px;
    left: 0.3rem;
    font-style: normal;
    font-size: 0.68rem;
    color: var(--text-3);
}

.playhead {
    position: absolute;
    bottom: 18px;
    width: 1px;
    height: 26px;
    background: var(--text);
}
.playhead::after {
    content: "";
    position: absolute;
    top: -4px; left: -3px;
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--text);
}

/* The unlisted tail of a partial tracklist. Hatched rather than filled: it is an
   absence of data, and a solid block would read as a section of the set. */
.ruler-gap {
    position: absolute;
    right: 0;
    bottom: 20px;
    height: 22px;
    background: repeating-linear-gradient(
        -45deg,
        rgba(216, 161, 58, 0.16) 0 4px,
        transparent 4px 8px
    );
    border-left: 1px dashed rgba(216, 161, 58, 0.45);
}
.ruler-mini .ruler-gap { bottom: calc(50% - 6px); height: 13px; }

.flag-partial {
    padding: 0.08rem 0.55rem;
    border: 1px solid rgba(216, 161, 58, 0.4);
    border-radius: 100px;
    background: var(--gold-soft);
    color: var(--gold);
    font-size: 0.72rem;
    letter-spacing: 0.03em;
    white-space: nowrap;
}
.set-note-partial { color: var(--gold); font-size: 0.9rem; }

.ruler-key {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.1rem;
    /* The hour labels hang below the ruler's baseline, so the key has to clear
       them. On an eight-hour set there are seven of them and they land straight
       through this row otherwise. */
    padding-top: 1.1rem;
    padding-bottom: 1rem;
    color: var(--text-3);
    font-size: 0.8rem;
}
.key { display: inline-flex; align-items: center; gap: 0.4rem; }
.key::before { content: ""; width: 3px; height: 12px; border-radius: 100px; }
.key-on::before { background: var(--brand-light); }
.key-off::before { background: var(--gold); height: 7px; }
.key-hint { margin-left: auto; color: var(--text-2); }

/* ---------- set page ---------- */

/* padding-top/bottom, NOT the shorthand. `.set` is also `.container`, whose
   whole job is `padding: 0 var(--pad)`, and a shorthand here resets the left and
   right to zero — so every set page ran flush to the screen edge on a phone
   while the index, which does not override it, did not. The gutter is var(--pad)
   because that is the one every other page already uses; a value picked
   specially for this page would be a second gutter to keep in step. */
.set { padding-top: 9.5rem; padding-bottom: 7rem; }

.eyebrow {
    color: var(--text-3);
    font-size: 0.8rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.set-title {
    margin-top: 0.6rem;
    font-size: clamp(2.1rem, 4.2vw, 3rem);
    font-weight: 300;
    letter-spacing: -0.032em;
    line-height: 1.1;
    color: #fff;
}
.set-event { margin-top: 0.3rem; color: var(--text-2); font-size: 1.125rem; }

.set-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 1.6rem;
    margin-top: 1.2rem;
    color: var(--text-3);
    font-size: 0.9rem;
    font-variant-numeric: tabular-nums;
}
.set-facts b { color: var(--text); font-weight: 400; }
.set-note { margin-top: 1rem; max-width: 44rem; color: var(--text-2); font-size: 0.98rem; }

/* The ruler and the player are one instrument: the ruler is the map, the player
   is the transport. Boxing them together is what makes clicking a mark feel like
   operating the thing directly below it. */
.deck {
    margin-top: 2.4rem;
    border: 1px solid var(--border);
    border-radius: 18px;
    background: rgba(255, 255, 255, 0.02);
    overflow: hidden;
}
.deck-top { padding: 1rem 1.4rem 0; }
.player { border-top: 1px solid var(--border); }
.player iframe { display: block; }

.set-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 16rem;
    gap: 3.5rem;
    margin-top: 3rem;
    align-items: start;
}

.list-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 1rem;
}
.list-head h2 { font-size: 1.15rem; font-weight: 400; letter-spacing: -0.02em; }
.list-meta { color: var(--text-3); font-size: 0.85rem; font-variant-numeric: tabular-nums; }

.rows {
    border: 1px solid var(--border);
    border-radius: 18px;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.02);
}
.row {
    display: grid;
    grid-template-columns: 2.4rem 5.4rem minmax(0, 1fr) auto;
    align-items: center;
    gap: 1rem;
    padding: 0.8rem 1.4rem;
    border-bottom: 1px solid var(--border);
    transition: background 140ms ease;
}
.row:last-child { border-bottom: none; }
.row:hover { background: rgba(255, 255, 255, 0.03); }
.row.on { background: rgba(112, 101, 240, 0.10); }

.row-n {
    color: var(--text-3);
    font-size: 0.95rem;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.row-cue {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.25rem 0.6rem;
    font: 400 0.85rem var(--font);
    font-variant-numeric: tabular-nums;
    color: var(--brand-lighter);
    background: rgba(112, 101, 240, 0.16);
    border: 1px solid rgba(112, 101, 240, 0.4);
    border-radius: 100px;
    cursor: pointer;
    transition: background 140ms ease;
}
.row-cue:hover { background: rgba(112, 101, 240, 0.3); }
.cue-play {
    width: 0; height: 0;
    border-left: 6px solid currentColor;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    opacity: 0.6;
}
.row-cue:hover .cue-play { opacity: 1; }

.row-text { min-width: 0; }
.row-title {
    display: block;
    color: var(--text);
    font-size: 0.98rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.row-artist {
    display: block;
    color: var(--text-3);
    font-size: 0.85rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.row.on .row-title { color: var(--brand-lighter); }

.row-id .row-title { color: var(--gold); letter-spacing: 0.06em; font-size: 0.9rem; }
.row-id .row-cue {
    color: var(--gold);
    background: var(--gold-soft);
    border-color: rgba(216, 161, 58, 0.4);
}
.row-id .row-cue:hover { background: rgba(216, 161, 58, 0.28); }

/* Two outlined buttons on every one of forty-nine rows is what makes a tracklist
   site look like a database dump. They stay in the tab order and stay tappable;
   they only take colour when the row is under the cursor. */
.row-links { display: flex; gap: 0.9rem; }
.mini { color: var(--text-3); font-size: 0.8rem; white-space: nowrap; transition: color 140ms ease; }
.row:hover .mini { color: var(--text-2); }
.mini:hover { color: var(--brand-lighter); }
.mini-off { font-style: italic; }
.row:hover .mini-off, .mini-off:hover { color: var(--text-3); }

/* ---------- sidebar ---------- */

.side { position: sticky; top: 6rem; display: grid; gap: 2rem; }
.side-block h3 {
    padding-bottom: 0.5rem;
    margin-bottom: 0.7rem;
    border-bottom: 1px solid var(--border);
    color: var(--text-3);
    font-size: 0.75rem;
    font-weight: 400;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}
.side-block dl { display: grid; grid-template-columns: 4.6rem minmax(0, 1fr); gap: 0.4rem 0.8rem; }
.side-block dt { color: var(--text-3); font-size: 0.82rem; }
.side-block dd { color: var(--text); font-size: 0.88rem; }
.side-note { color: var(--text-2); font-size: 0.85rem; line-height: 1.6; }
.side-note + .side-note { margin-top: 0.7rem; }
.side-note a { color: var(--brand-light); }
.side-note a:hover { color: var(--brand-lighter); }

.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.chip {
    padding: 0.15rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: 100px;
    color: var(--text-2);
    font-size: 0.78rem;
}

/* ---------- charts ---------- */

.crows {
    border: 1px solid var(--border);
    border-radius: 18px;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.02);
}
.crow {
    display: grid;
    grid-template-columns: 2.6rem minmax(0, 1.4fr) minmax(0, 1fr) 8rem 4.4rem;
    align-items: center;
    gap: 1rem;
    padding: 0.85rem 1.4rem;
    border-bottom: 1px solid var(--border);
    transition: background 140ms ease;
}
.crow:last-child { border-bottom: none; }
.crow:hover { background: rgba(255, 255, 255, 0.03); }
.crow-n {
    color: var(--text-3);
    font-size: 1.05rem;
    font-variant-numeric: tabular-nums;
    text-align: right;
}
.crow:nth-child(-n+3) .crow-n { color: var(--gold); }
.crow-text { min-width: 0; }
.crow-text b {
    display: block;
    color: var(--text);
    font-weight: 400;
    font-size: 0.98rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.crow-text span { display: block; color: var(--text-3); font-size: 0.85rem; }
.crow-where { color: var(--text-3); font-size: 0.85rem; }

.bar { display: block; height: 5px; border-radius: 100px; background: rgba(255, 255, 255, 0.07); overflow: hidden; }
.bar span {
    display: block;
    height: 100%;
    border-radius: 100px;
    background: linear-gradient(90deg, rgba(112, 101, 240, 0.55), var(--brand-light));
}
.crow-v { text-align: right; color: var(--text); font-size: 0.9rem; font-variant-numeric: tabular-nums; }

/* ---------- the trending chart ---------- */

.eyebrow {
    margin-bottom: 0.9rem;
    color: var(--brand-light);
    font-size: 0.8rem; letter-spacing: 0.12em; text-transform: uppercase;
}
.page-sub a { color: var(--brand-light); }
.page-sub a:hover { color: var(--brand-lighter); }

/* Genre tabs: the index's chips, as links, so every genre chart is a real page. */
.chart-tabs { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-bottom: 1.6rem; }
.chart-tabs .filter { text-decoration: none; }

.crows-trend .crow {
    grid-template-columns: 2.6rem 3.2rem minmax(0, 1.5fr) minmax(0, 1fr) 7rem 4.6rem;
}
.crows-trend .crow-where { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Inside .crow-text every span is a block (title line, artist line); the tags
   are the exception and need the more specific rule to say so. */
.crow-text .crow-tags { display: inline-flex; gap: 0.3rem; margin-left: 0.5rem; vertical-align: 1px; }
.crow-text .crow-tags > span { display: inline-block; margin-left: 0; }
.crow-move {
    font-size: 0.78rem; font-variant-numeric: tabular-nums; white-space: nowrap;
    color: var(--text-3);
}
.crow-move.up { color: #7fd1a3; }
.crow-move.down { color: #e08a8a; }
.crow-move.new { color: var(--brand-light); letter-spacing: 0.06em; text-transform: uppercase; font-size: 0.7rem; }
.crow-text b a { color: inherit; text-decoration: none; }
.crow-text b a:hover { color: var(--brand-lighter); }
.crow-v small { display: block; color: var(--text-3); font-size: 0.74rem; }
.tag-dim {
    display: inline-block; margin-left: 0.4rem; padding: 0.08rem 0.5rem;
    border: 1px solid var(--border-2); border-radius: 100px;
    color: var(--text-3); font-size: 0.7rem; letter-spacing: 0.03em; white-space: nowrap;
}
.tag-unrel {
    display: inline-block; margin-left: 0.4rem; padding: 0.08rem 0.5rem;
    border: 1px solid rgba(159, 163, 252, 0.45); border-radius: 100px;
    background: rgba(112, 101, 240, 0.14); color: var(--brand-light);
    font-size: 0.7rem; letter-spacing: 0.03em; white-space: nowrap;
}
@media (max-width: 900px) {
    /* Rank and movement stack in one narrow column; the title gets the width. */
    .crows-trend .crow {
        grid-template-columns: 2.6rem minmax(0, 1fr) auto;
        grid-template-areas: "n text v";
        gap: 0.6rem 0.9rem;
    }
    .crows-trend .crow-n { grid-area: n; text-align: left; }
    .crows-trend .crow-move { grid-area: n; align-self: end; font-size: 0.68rem; margin-top: 1.4rem; }
    .crows-trend .crow-move.same { display: none; }
    .crows-trend .crow-text { grid-area: text; }
    .crows-trend .crow-v { grid-area: v; }
    .crows-trend .crow-text b { white-space: normal; }
    .crow-text .crow-tags { display: flex; margin: 0.25rem 0 0; }
}
@media (max-width: 760px) {
    /* One row that scrolls sideways, not four rows of chips before the chart. */
    .chart-tabs {
        flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
        margin-left: calc(-1 * var(--pad)); margin-right: calc(-1 * var(--pad));
        padding: 0 var(--pad) 0.5rem;
        scrollbar-width: none;
    }
    .chart-tabs::-webkit-scrollbar { display: none; }
    .chart-tabs .filter { flex: none; }
}

.tag-same {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.08rem 0.5rem;
    border: 1px solid rgba(216, 161, 58, 0.4);
    border-radius: 100px;
    background: var(--gold-soft);
    color: var(--gold);
    font-size: 0.7rem;
    letter-spacing: 0.03em;
    white-space: nowrap;
}

/* ---------- search hits ---------- */

/* The records a search matched, above the set cards. A record is not a set,
   so it does not get a card: one line for the record, one for where it was
   played, with each cue a link straight to the row on that set page. */
.hits { margin-bottom: 1.2rem; }
.hits-head {
    margin-bottom: 0.6rem;
    color: var(--text-3); font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase;
}
.hit-list {
    list-style: none;
    border: 1px solid var(--border); border-radius: 18px; overflow: hidden;
    background: rgba(255, 255, 255, 0.02);
}
.hit {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.4rem 1.2rem;
    align-items: baseline;
    padding: 0.8rem 1.4rem; border-bottom: 1px solid var(--border);
}
.hit:last-child { border-bottom: none; }
.hit-title { color: var(--text); font-size: 0.98rem; }
.hit-title span { color: var(--text-3); font-size: 0.85rem; margin-left: 0.5rem; }
.hit-count { color: var(--text-2); font-size: 0.85rem; white-space: nowrap; font-variant-numeric: tabular-nums; }
.hit-sets { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 0.35rem 1.1rem; font-size: 0.85rem; }
.hit-sets a { color: var(--brand-light); text-decoration: none; white-space: nowrap; }
.hit-sets a:hover { color: var(--brand-lighter); }
.hit-sets a b { font-weight: 400; color: var(--text-2); }
.hit-sets .more { color: var(--text-3); }
.hits-status { color: var(--text-3); font-size: 0.85rem; padding: 0.4rem 0; }
@media (max-width: 760px) {
    .hit { padding: 0.75rem 1rem; }
}

/* ---------- record pages ---------- */

.row-title { text-decoration: none; }
a.row-title:hover { color: var(--brand-lighter); }
.rec-when { margin-top: 1.2rem; color: var(--text-3); font-size: 0.85rem; }
.rec-body { display: grid; grid-template-columns: minmax(0, 1fr) 20rem; gap: 3rem; align-items: start; }
.rec-side { position: sticky; top: 6rem; }
.plays {
    list-style: none;
    border: 1px solid var(--border); border-radius: 18px; overflow: hidden;
    background: rgba(255, 255, 255, 0.02);
}
.play {
    display: grid; grid-template-columns: 5.2rem minmax(0, 1fr) auto;
    gap: 1rem; align-items: center;
    padding: 0.8rem 1.2rem; border-bottom: 1px solid var(--border);
}
.play:last-child { border-bottom: none; }
.play:hover { background: rgba(255, 255, 255, 0.03); }
.play-cue {
    display: inline-flex; justify-content: center;
    padding: 0.3rem 0.6rem; border-radius: 100px;
    background: rgba(112, 101, 240, 0.16); color: var(--brand-lighter);
    font-size: 0.85rem; font-variant-numeric: tabular-nums; text-decoration: none;
}
.play-cue:hover { background: var(--brand); color: #fff; }
.play-text { min-width: 0; }
.play-text b { display: block; font-weight: 400; color: var(--text); }
.play-text b a { color: inherit; text-decoration: none; }
.play-text b a:hover { color: var(--brand-lighter); }
.play-text span { display: block; color: var(--text-3); font-size: 0.85rem; }
.play-date { color: var(--text-3); font-size: 0.85rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.stands { list-style: none; display: grid; gap: 0.5rem; }
.stand {
    display: grid; grid-template-columns: 2.4rem 3rem minmax(0, 1fr);
    gap: 0.4rem 0.8rem; align-items: baseline;
    padding: 0.7rem 0.9rem; border: 1px solid var(--border); border-radius: 12px;
    background: var(--surface);
}
.stand .crow-n { text-align: left; color: var(--text); }
.stand-chart { color: var(--text); text-decoration: none; font-size: 0.98rem; }
.stand-chart:hover { color: var(--brand-lighter); }
.stand-window { grid-column: 3; color: var(--text-3); font-size: 0.78rem; }
.rec-links { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.hit-title a { color: inherit; text-decoration: none; }
.hit-title a:hover { color: var(--brand-lighter); }
@media (max-width: 900px) {
    .rec-body { grid-template-columns: minmax(0, 1fr); gap: 2.4rem; }
    .rec-side { position: static; }
}
@media (max-width: 600px) {
    .play { grid-template-columns: 4.6rem minmax(0, 1fr); padding: 0.75rem 1rem; }
    .play-date { grid-column: 2; }
}

/* ---------- prose ---------- */

.prose { max-width: 44rem; padding-bottom: 7rem; }
.prose h2 { margin-top: 2.6rem; font-size: 1.15rem; font-weight: 400; letter-spacing: -0.02em; }
.prose h2:first-child { margin-top: 0; }
.prose p { margin-top: 0.7rem; color: var(--text-2); font-size: 1rem; line-height: 1.7; }
.prose a { color: var(--brand-light); }
.prose a:hover { color: var(--brand-lighter); }

/* ---------- footer ---------- */

.foot { border-top: 1px solid var(--border); }
.foot-in {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 1.5rem;
    padding-top: 2.2rem;
    padding-bottom: 2.8rem;
}
.foot p { max-width: 42rem; color: var(--text-3); font-size: 0.82rem; line-height: 1.65; }
/* The blurb and the notice are one flex item, so the links stay pinned to the
   far edge rather than becoming the middle of three. */
.foot-copy { margin-top: 0.9rem; font-size: 0.78rem; }
.foot-links { display: flex; gap: 1.4rem; }
.foot-links a { color: var(--text-2); font-size: 0.85rem; }
.foot-links a:hover { color: var(--text); }

/* ---------- responsive ---------- */

@media (max-width: 900px) {
    .set-body { grid-template-columns: minmax(0, 1fr); gap: 2.4rem; }
    .side { position: static; }
    .crow { grid-template-columns: 2.2rem minmax(0, 1fr) 4.6rem; }
    .crow-where, .bar { display: none; }
}

@media (max-width: 760px) {
    :root { --pad: 1.25rem; }
    /* Three columns with a centred mark do not fit: the mark goes left and the
       CTA drops, which keeps the links reachable rather than scrolling the
       header sideways. */
    /* Two rows rather than dropping anything: wordmark and CTA on the first,
       links underneath. Hiding the links to make one row fit is what removes
       the only route to Charts and About from the header. */
    .nav-inner {
        grid-template-columns: 1fr auto;
        row-gap: 0.85rem;
        padding: 1rem var(--pad);
    }
    .nav-logo { grid-column: 1; grid-row: 1; justify-self: start; }
    .nav-cta { grid-column: 2; grid-row: 1; }
    .nav-links {
        grid-column: 1 / -1;
        grid-row: 2;
        justify-self: start;
        gap: 1.4rem;
    }
    .nav-links a { font-size: 0.9rem; }

    .page-hero { padding: 7.5rem var(--pad) 3rem; }
    .set { padding-top: 6.5rem; }
    .hero-stats { gap: 1.6rem; }

    .row { grid-template-columns: 2rem 4.8rem minmax(0, 1fr); gap: 0.7rem; padding: 0.75rem 1rem; }
    .row-links { grid-column: 2 / -1; }
    .crow { padding: 0.8rem 1rem; }
    .card-top { flex-direction: column; gap: 0.4rem; }
    .card-date { text-align: left; }
    .card-genres { margin-left: 0; }
    #q { width: 100%; }
    .controls { width: 100%; }
    .key-hint { display: none; }
}

/* At 320px the wordmark, its module label and three links do not fit one row.
   The label goes rather than the links: the page below always names the section,
   whereas a clipped nav scrolls the whole document sideways. */
@media (max-width: 420px) {
    .nav-links { gap: 1.05rem; }
    .nav-links a { font-size: 0.84rem; }
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after { transition-duration: 0.01ms !important; }
}

/* A YouTube set. The SoundCloud embed is a fixed 300px bar; a video needs its
   own aspect or it letterboxes itself inside a short box. */
.player-yt {
    position: relative;
    aspect-ratio: 16 / 9;
    background: #000;
}
.player-yt iframe { position: absolute; inset: 0; width: 100%; height: 100%; }

/* The caveat under the crossover chart. Gold because it is the same "treat this
   as uncertain" the ID marks mean elsewhere on the site. */
.chart-caveat {
    padding-left: 0.9rem;
    border-left: 2px solid rgba(216, 161, 58, 0.5);
    color: var(--text-3);
}

/* ---------- the persistent player ----------
   One element, body-level, alive for the whole visit. Two shapes:

   .np-docked  absolutely positioned over #player-slot on the set page it is
               playing. Absolute, not fixed, so it scrolls with the page and
               needs nothing kept in sync on scroll.
   .np-mini    a card bottom-left on desktop, a full-width bar on mobile, for
               every other page.

   The iframe inside is never moved between the two - moving an iframe reloads
   it - only the container's position and size change. */

.np {
    position: absolute;
    z-index: 90;
    border-radius: 18px;
    overflow: hidden;
    background: #0d0d0f;
    border: 1px solid var(--border);
    transition: box-shadow 200ms ease;
}
.np-hidden { display: none; }

/* The slot reserves the space; the player sits over it. Its own play button is
   hidden once filled, so the two never show at once. */
.player-slot { position: relative; min-height: 300px; }
.player-slot.slot-filled .slot-play { display: none; }

/* The stand-in embed shown when a DIFFERENT set is playing. It fills the slot
   exactly as the real player does when docked, so the page looks the same
   either way and nothing shifts when the handover happens. */
.slot-preview {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
}

.slot-play {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.8rem;
    width: 100%;
    background: rgba(255, 255, 255, 0.02);
    border: none;
    color: var(--text-2);
    font: 400 1rem var(--font);
    cursor: pointer;
    transition: background 140ms ease, color 140ms ease;
}
.slot-play:hover { background: rgba(112, 101, 240, 0.10); color: var(--text); }
.slot-play-icon {
    width: 0; height: 0;
    border-left: 15px solid var(--brand-light);
    border-top: 10px solid transparent;
    border-bottom: 10px solid transparent;
}

.np-frame { width: 100%; height: 100%; }
.np-frame iframe { display: block; width: 100%; height: 100%; border: 0; }

/* Docked: the embed fills it and the bar is redundant - the set page already
   names the set in its own headline. */
.np-docked { border-radius: 18px; }
.np-docked .np-bar { display: none; }

/* Mini: the embed collapses to nothing and the bar is the whole control. */
.np-mini {
    position: fixed;
    left: 1.5rem;
    bottom: 1.5rem;
    top: auto;
    width: 21rem;
    height: auto;
    box-shadow: 0 20px 50px rgba(0, 0, 0, 0.55);
}
.np-mini .np-frame {
    /* Not display:none - a hidden iframe is throttled or stopped by some
       browsers. One pixel, clipped, keeps it a live element. */
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.np-bar {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.7rem 0.8rem;
}
.np-toggle {
    flex: none;
    width: 2.1rem; height: 2.1rem;
    display: grid;
    place-items: center;
    border: none;
    border-radius: 100px;
    background: var(--brand);
    cursor: pointer;
}
.np-toggle:hover { background: var(--brand-light); }
.np-icon {
    width: 0; height: 0;
    margin-left: 2px;
    border-left: 9px solid #fff;
    border-top: 6px solid transparent;
    border-bottom: 6px solid transparent;
}
.np-playing .np-icon {
    margin-left: 0;
    width: 9px; height: 11px;
    border: none;
    border-left: 3px solid #fff;
    border-right: 3px solid #fff;
}
.np-meta { min-width: 0; flex: 1; line-height: 1.25; }
.np-artist {
    display: block;
    color: var(--text);
    font-size: 0.88rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.np-event {
    display: block;
    color: var(--text-3);
    font-size: 0.76rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.np-time {
    flex: none;
    color: var(--text-3);
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
}
.np-close {
    flex: none;
    width: 1.6rem; height: 1.6rem;
    border: none;
    background: none;
    color: var(--text-3);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
    border-radius: 100px;
}
.np-close:hover { color: var(--text); background: rgba(255, 255, 255, 0.06); }

/* Mobile: a bottom bar across the full width, the way a music app does it.
   The footer gets padding so the bar never covers the last row of a tracklist. */
@media (max-width: 760px) {
    .np-mini {
        left: 0; right: 0; bottom: 0;
        width: auto;
        border-radius: 0;
        border-left: none; border-right: none; border-bottom: none;
        box-shadow: 0 -10px 30px rgba(0, 0, 0, 0.5);
    }
    body:has(.np-mini:not(.np-hidden)) .foot { padding-bottom: 4.5rem; }
    .player-slot { min-height: 210px; }
}

/* ---------- artwork ---------- */

.card-head { display: flex; align-items: center; gap: 1rem; min-width: 0; }
.card-art {
    flex: none;
    width: 56px; height: 56px;
    border-radius: 10px;
    object-fit: cover;
    background: var(--surface-2);
    border: 1px solid var(--border);
}

.np-art {
    flex: none;
    width: 40px; height: 40px;
    border-radius: 7px;
    object-fit: cover;
    background: var(--surface-2);
}
.np-art[hidden] { display: none; }

/* The slot's own button is the fallback for "something else is playing", so it
   sits quietly at the bottom rather than filling the frame. */
.slot-play {
    inset: auto 0 0 0;
    height: auto;
    padding: 0.7rem;
    background: rgba(17, 18, 18, 0.86);
    border-top: 1px solid var(--border);
    font-size: 0.88rem;
}
.slot-play-icon {
    border-left-width: 10px;
    border-top-width: 7px;
    border-bottom-width: 7px;
}

@media (max-width: 760px) {
    .card-art { width: 44px; height: 44px; }
}

/* The arrow inside the pale chip is the one saturated moment in the header,
   same as on nine71.ai. */
.btn-arrow { display: inline-flex; align-items: center; }
.btn-light .btn-arrow { color: var(--brand); }

/* ---------- header, matched to nine71.ai ----------
   Two components sharing one piece of markup, exactly as the marketing site
   does it. At the top of the page: links STACKED at the left, wordmark centred,
   CTA at the right, sitting directly on the page with no container. Once
   scrolled: they collapse into a narrow centred floating panel.

   The stacked-at-rest state is the distinctive half and the easy one to mistake
   for a broken flex row - it is deliberate. Values here are lifted from
   site/styles.css rather than re-guessed; if the two disagree, that file wins. */

.nav-inner {
    align-items: start;
    padding: 1.75rem var(--pad);
    border: 1px solid transparent;
    transition: max-width 260ms ease, padding 260ms ease,
                background 260ms ease, border-color 260ms ease;
}

.nav-links {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.42rem;          /* 26px line-to-line on the reference */
}

.nav-links a { font-size: 0.875rem; line-height: 1.4; }

.nav-logo { padding-top: 0.15rem; }
.nav-logo .wordmark-img { height: 16px; }

/* At rest the header has no ground of its own. */
.nav { border-bottom: 0; }
.nav.scrolled {
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom-color: transparent;
}

/* --- scrolled: the floating panel --- */
.nav.scrolled .nav-inner {
    max-width: 38rem;
    margin-top: 0.6rem;
    padding: 0.4rem 0.4rem 0.4rem 1.1rem;
    /* Translucent, not opaque - it lifts visibly over anything but flat black. */
    background: rgba(17, 18, 18, 0.55);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border-color: var(--nav-border);
    border-radius: 10px;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 1.1rem;
}
.nav.scrolled .nav-logo { grid-column: 1; justify-self: start; padding-top: 0; }
.nav.scrolled .nav-logo .wordmark-img { height: 14px; }
.nav.scrolled .nav-links {
    grid-column: 2;
    justify-self: start;
    flex-direction: row;
    align-items: center;
    gap: 1.3rem;
}
.nav.scrolled .nav-cta { grid-column: 3; }
.nav.scrolled .nav-cta .btn { padding: 0.5rem 1rem; }
/* The chevron belongs to the at-rest chip only; inside the panel it is plain. */
.nav.scrolled .nav-cta .btn-arrow { display: none; }
/* The module label would push the panel wide; the wordmark carries it there. */
.nav.scrolled .nav-mod { display: none; }

/* Mobile: the stacked-at-rest links are a desktop idea. Fixed-position and
   three items tall, they run straight over the hero. nine71.ai solves this with
   a hamburger drawer; with three links a plain row on a second line is simpler
   and needs no toggle to reach Charts and About. */
@media (max-width: 760px) {
    .nav-links { flex-direction: row; align-items: center; gap: 1.4rem; }
    .nav-inner { align-items: center; padding: 1rem var(--pad); }
    .nav-logo { padding-top: 0; }
    /* The at-rest header is ~5.5rem tall here; the hero has to start below it. */
    .page-hero { padding-top: 8.5rem; }
    .set { padding-top: 7.5rem; }
}

/* At rest the header spans the viewport, not the 1200px content container:
   pinned to the container the links and the CTA drift inward on a wide screen
   and read as centred rather than as edges. The wordmark stays centred because
   the outer columns are 1fr each, so widening the grid moves the ends outward
   and leaves the middle alone.

   The scrolled panel keeps its own narrow max-width - that one IS a centred
   object, and is meant to be. */
.nav-inner { max-width: none; }

/* Mobile scrolled state. The desktop scrolled header is a narrow centred
   floating panel on ONE row - logo, links, CTA. At 390px that row cannot fit:
   the CTA wraps to two lines, the links fall out of alignment and the panel
   doubles in height. So mobile keeps the two-row layout it already has at rest
   and only gains the ground it needs to stop content showing through it. */
@media (max-width: 760px) {
    .nav.scrolled .nav-inner {
        max-width: none;
        margin-top: 0;
        padding: 0.75rem var(--pad);
        border-radius: 0;
        border-color: transparent;
        border-bottom-color: var(--border);
        grid-template-columns: 1fr auto;
        row-gap: 0.7rem;
        align-items: center;
        background: rgba(17, 18, 18, 0.82);
    }
    .nav.scrolled .nav-logo { grid-column: 1; grid-row: 1; }
    .nav.scrolled .nav-logo .wordmark-img { height: 16px; }
    .nav.scrolled .nav-cta { grid-column: 2; grid-row: 1; }
    .nav.scrolled .nav-links {
        grid-column: 1 / -1;
        grid-row: 2;
        flex-direction: row;
        gap: 1.4rem;
    }
    /* Two words on one line - the wrap is what made the chip twice as tall. */
    .nav-cta .btn { white-space: nowrap; padding: 0.45rem 0.9rem; font-size: 0.82rem; }
    .nav.scrolled .nav-mod { display: inline; }

    /* Scrolling down takes the header away; scrolling up brings it back.
       A phone screen is short and this header is two rows tall, so held on
       screen it costs about a fifth of the viewport for the whole length of a
       139-track set. It is a MOBILE behaviour only: the rule lives inside this
       media query, so the class app.js toggles does nothing on a desktop, where
       the header is one row and there is room for it. */
    .nav { transition: transform 220ms ease, background 200ms ease, border-color 200ms ease; }
    .nav.nav-hidden { transform: translateY(-100%); }
}

/* Sliding a fixed header is exactly the motion this setting exists to stop. */
@media (prefers-reduced-motion: reduce) {
    .nav { transition: none; }
}


/* ===========================================================================
   Contributions: the sign-in, the forms, and the credit under a track.
   ---------------------------------------------------------------------------
   Everything here styles something account.js draws. Nothing in this block is
   needed to read a tracklist — with the module blocked none of these selectors
   match anything, which is the same progressive contract the rest of the site
   keeps.

   No new colour is introduced. Violet is identified, gold is unidentified, and
   that pairing already carries meaning on the ruler and in the charts; a third
   accent for "submitted" would quietly claim to be a third kind of fact.
   =========================================================================== */

.muted { color: var(--text-2); font-size: 0.92rem; line-height: 1.6; }
.muted a { color: var(--brand-light); }
.muted a:hover { color: var(--brand-lighter); }
.tiny { font-size: 0.8rem; }
.mt { margin-top: 1.6rem; }

/* A button that reads as a link. Used where the action is secondary to the one
   beside it and a second filled button would make neither look primary. */
.linkish {
    background: none; border: 0; padding: 0; cursor: pointer;
    font: inherit; font-size: 0.85rem; color: var(--brand-light);
    text-align: left;
}
.linkish:hover { color: var(--brand-lighter); }

/* ------------------------------------------------------------ the nav chip */

.nav-account { display: inline-flex; align-items: center; }
/* Sign in and the account name share one shape with the CTA beside them: same
   height, same radius, same weight. Outlined rather than filled so the pair
   still has exactly one primary control. */
.nav-signin, .nav-me {
    display: inline-flex; align-items: center; gap: 0.45rem;
    box-sizing: border-box; height: 2.25rem; padding: 0 1rem;
    border: 1px solid var(--border-2); border-radius: 6px;
    background: transparent;
    font: 500 0.88rem var(--font); color: var(--text);
    cursor: pointer; white-space: nowrap; text-decoration: none;
    transition: border-color 140ms ease, color 140ms ease, background 140ms ease;
}
.nav-signin:hover, .nav-me:hover { border-color: var(--brand-light); color: var(--brand-lighter); }
/* The CTA beside it takes the same height, so the pair sits on one line. */
.nav-cta .btn { box-sizing: border-box; height: 2.25rem; padding-top: 0; padding-bottom: 0; }
.nav.scrolled .nav-signin, .nav.scrolled .nav-me,
.nav.scrolled .nav-cta .btn { height: 2.125rem; padding-left: 0.9rem; padding-right: 0.9rem; }
@media (max-width: 760px) {
    .nav-cta { gap: 0.45rem; }
    .nav-signin, .nav-me, .nav-cta .btn { height: 2rem; padding-left: 0.8rem; padding-right: 0.8rem; font-size: 0.82rem; }
}
/* One dot, no number: the count is on the account page and a badge here would
   be a notification for something nobody is waiting on. */
.nav-me .dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--gold);
}

/* ------------------------------------------------------- the amber row's ask */

/* Sits beside "Not identified" and only appears once account.js has run — the
   markup ships it hidden, so a reader without scripting sees the label alone
   rather than a button that cannot work. */
.mini-id {
    background: none; border: 0; padding: 0; cursor: pointer;
    font: inherit; font-size: 0.8rem; color: var(--gold);
    white-space: nowrap;
}
.mini-id:hover { color: var(--brand-lighter); }

/* The row a link pointed at, on arrival. A full load gets :target for free;
   a soft navigation adds the class. Same tint either way. */
.row:target, .row.row-target { background: rgba(112, 101, 240, 0.09); }

/* RIGHT OR WRONG. Two small buttons beside the links, filled by account.js
   with the counts and the reader's own vote. Hidden until then: without
   scripting a vote cannot be cast, so nothing offers one. */
.row-votes { display: inline-flex; gap: 0.3rem; align-items: center; margin-left: 0.6rem; }
.vote {
    display: inline-flex; align-items: center; gap: 0.3rem;
    padding: 0.2rem 0.5rem; border: 1px solid var(--border); border-radius: 100px;
    background: transparent; color: var(--text-3); font: inherit; font-size: 0.74rem;
    cursor: pointer; font-variant-numeric: tabular-nums;
}
.vote b { font-weight: 400; color: var(--text-2); }
.vote:hover { border-color: var(--border-2); color: var(--text); }
.vote.on[data-vote="1"] { border-color: #7fd1a3; color: #7fd1a3; }
.vote.on[data-vote="-1"] { border-color: #e08a8a; color: #e08a8a; }
.row-credit a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--border-2); }
.row-credit a:hover { color: var(--text-2); }

/* WHO FOUND IT. Under the track, quieter than the artist line: the credit is a
   fact about the ID, not part of the record's name. */
.row-credit {
    display: block; margin-top: 0.2rem;
    font-size: 0.76rem; color: var(--text-3); letter-spacing: 0.01em;
}
/* An ID accepted since the last deploy, drawn live rather than built in. Gold,
   because until the corpus carries it the row is still an unidentified row. */
.row-credit-new { color: var(--gold); }

/* --------------------------------------------------------------- the sheet */

dialog.sheet {
    width: min(30rem, calc(100vw - 2rem));
    margin: auto;                       /* the reset above zeroed it */
    max-height: calc(100vh - 2rem);
    overflow: auto;
    padding: 0; border: 1px solid var(--border);
    border-radius: 14px; background: var(--surface); color: var(--text);
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);
}
/* On a phone it is a bottom sheet: thumb reach, and the keyboard pushes a
   centred panel off the top of the screen. */
@media (max-width: 600px) {
    dialog.sheet {
        width: 100vw; max-width: none;
        margin: auto 0 0;
        max-height: 92vh;
        border-radius: 16px 16px 0 0; border-bottom: 0;
    }
}
dialog.sheet::backdrop { background: rgba(0, 0, 0, 0.62); }
.sheet-in { padding: 1.7rem 1.6rem 1.5rem; }
@media (max-width: 600px) { .sheet-in { padding: 1.5rem 1.25rem calc(1.5rem + env(safe-area-inset-bottom)); } }
.sheet-title { font-size: 1.15rem; font-weight: 400; letter-spacing: -0.02em; }
.sheet-note { margin-top: 0.6rem; color: var(--text-2); font-size: 0.85rem; line-height: 1.6; }
.sheet-note a { color: var(--brand-light); }
.sheet-error {
    margin-top: 0.9rem; padding: 0.6rem 0.75rem;
    border: 1px solid var(--gold); border-radius: 8px;
    background: var(--gold-soft); color: var(--text);
    font-size: 0.82rem;
}
.sheet-actions {
    display: flex; align-items: center; gap: 1.1rem;
    margin-top: 1.4rem; flex-wrap: wrap;
}
.sheet-actions .btn { padding: 0.65rem 1.3rem; }
.sheet-form .linkish.tiny { display: inline-block; margin-top: 0.9rem; }
.sheet-actions a.linkish { text-decoration: none; }

/* ---------------------------------------------------------------- fields */

.field { display: block; margin-top: 1rem; }
.field > span {
    display: block; margin-bottom: 0.35rem;
    font-size: 0.78rem; color: var(--text-2);
    text-transform: uppercase; letter-spacing: 0.08em;
}
/* "required" and "optional" are said on the label rather than enforced only by
   the browser, because a form that refuses on submit without having said which
   field was needed is a form people abandon. */
.field .req { color: var(--gold); font-weight: 400; letter-spacing: 0.06em; }
.field i { font-style: normal; color: var(--text-3); text-transform: none; letter-spacing: 0; }
.field input, .field select, .field textarea {
    width: 100%; padding: 0.6rem 0.7rem;
    background: var(--bg); color: var(--text);
    border: 1px solid var(--border); border-radius: 8px;
    font: inherit; font-size: 0.92rem;
}
.field input:focus, .field select:focus, .field textarea:focus {
    outline: none; border-color: var(--brand-light);
}
.field textarea { resize: vertical; }
.field textarea.mono { font: 0.86rem/1.5 ui-monospace, "SF Mono", Menlo, monospace; white-space: pre; overflow-x: auto; }

.check { display: flex; align-items: center; gap: 0.55rem; margin-top: 1rem; font-size: 0.9rem; }
.check input { accent-color: var(--brand); }
.self-claim-box {
    margin-top: 0.6rem; padding: 0.8rem 0.9rem;
    border: 1px solid var(--border); border-radius: 10px;
    background: var(--surface-2);
}
.form-error {
    margin-top: 0.9rem; color: var(--gold); font-size: 0.82rem;
}

/* ------------------------------------------------------------- /signin/ */

.auth-card {
    max-width: 26rem; margin: 0 auto;
    padding: 1.6rem 1.5rem 1.5rem;
    border: 1px solid var(--border); border-radius: 14px; background: var(--surface);
}
.auth-card .field:first-child { margin-top: 0; }
.auth-card .sheet-actions .btn { padding: 0.7rem 1.4rem; }
.auth-card .linkish.tiny { display: inline-block; margin-top: 0.9rem; }
.auth-card .auth-nojs { margin-top: 1.2rem; font-size: 0.82rem; }
.auth-card .auth-nojs a { color: var(--brand-light); }

/* ------------------------------------------------------------- /submit/ */

.submit-grid {
    display: grid; gap: 1.2rem;
    grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
}
.submit-card {
    padding: 1.4rem 1.3rem;
    border: 1px solid var(--border); border-radius: 14px;
    background: var(--surface);
}
.submit-card h2 { font-size: 1.05rem; font-weight: 400; letter-spacing: -0.02em; margin-bottom: 0.5rem; }
.submit-card .btn { margin-top: 1.1rem; }
/* The notice above the cards: one line of why, one button. It is a bar, not a
   third card, so the two real cards keep their pair. */
.submit-notice {
    display: flex; align-items: center; justify-content: space-between; gap: 1.2rem;
    margin-bottom: 1.2rem; padding: 1rem 1.3rem;
    border: 1px solid var(--border); border-radius: 12px;
    background: var(--surface-2);
}
.submit-notice p { color: var(--text-2); font-size: 0.92rem; line-height: 1.55; }
.submit-notice a { color: var(--brand-light); }
.submit-notice a.btn { color: #121212; text-decoration: none; }
.submit-notice .btn { flex: none; }
@media (max-width: 600px) {
    .submit-notice { flex-direction: column; align-items: flex-start; }
}

/* ------------------------------------------------------------ /account/ */

.acct-grid {
    display: grid; gap: 1.2rem;
    grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
}
.acct-card {
    padding: 1.4rem 1.3rem;
    border: 1px solid var(--border); border-radius: 14px;
    background: var(--surface);
}
.acct-card h2 { font-size: 1.05rem; font-weight: 400; letter-spacing: -0.02em; }
.acct-card .btn, .acct-card form button { margin-top: 1rem; }
.acct-wide { grid-column: 1 / -1; }

.table-wrap { overflow-x: auto; margin-top: 1rem; }
.acct-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.acct-table th {
    text-align: left; padding: 0.5rem 0.7rem 0.5rem 0;
    font-size: 0.72rem; font-weight: 400; color: var(--text-3);
    text-transform: uppercase; letter-spacing: 0.08em;
    border-bottom: 1px solid var(--border);
}
.acct-table td {
    padding: 0.7rem 0.7rem 0.7rem 0; vertical-align: top;
    border-bottom: 1px solid var(--nav-border); color: var(--text-2);
}
.acct-table a { color: var(--brand-light); }

/* The four states, in the two colours the site already uses for exactly this
   distinction: violet is settled and gold is open. 'Not used' and 'Already had
   it' are deliberately not red — a rejected submission is not an error, and
   colouring it like one would make sending the next one feel like a risk. */
.st { white-space: nowrap; }
.st-pending { color: var(--gold); }
.st-accepted { color: var(--brand-light); }
.st-rejected, .st-duplicate { color: var(--text-3); }
.st .why {
    display: block; margin-top: 0.25rem;
    color: var(--text-3); font-size: 0.78rem; white-space: normal;
}
.chip-tiny { font-size: 0.68rem; padding: 0.1rem 0.4rem; vertical-align: middle; }

/* The unverified-email card. Gold, not red: an unverified address is a step not
   yet taken, not an error — and the one action it blocks (claiming a record) is
   named on the card rather than discovered by pressing a dead checkbox. */
.acct-warn { border-color: var(--gold); background: var(--gold-soft); }
