/* Indie Time — black / red / white, gig-poster energy (Kris's brand). */
:root {
  --red: #e8232a;
  --red-dark: #a3121a;
  --ink: #000;
  --panel: rgba(17,17,17,.5); /* 50% glass (Kris, 2026-11-12): the visualizer ghosts through content containers */
  --panel2: #1a1a1a;
  --line: #2c2c2c;
  --txt: #f2f2f2;
  --dim: #b9b9b9;
  --gold: #f5c542;
}
* { box-sizing: border-box; }
html { background: var(--ink); }
body {
  margin: 0; background: var(--ink); color: var(--txt);
  font-family: "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
  font-size: 17px; line-height: 1.45;
}
a { color: var(--red); text-decoration: none; }
a:hover { text-decoration: underline; }

/* header */
.site-head {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 10px 18px; border-bottom: 3px solid var(--red);
  background: #050505; position: sticky; top: 0; z-index: 40;
}
.brand img { height: 44px; display: block; }
/* Mature Audiences badge (Kris, 2026-11-09): just left of the nav
   logo, exactly the logo's height in the bar. */
.mature-badge { height: 44px; width: auto; display: block; }
.main-nav { display: flex; gap: 16px; flex-wrap: wrap; margin-left: auto; align-items: center; }
.main-nav a {
  color: var(--txt); text-transform: uppercase; letter-spacing: .06em;
  font-weight: 700; font-size: 14px;
}
.main-nav a:hover { color: var(--red); text-decoration: none; }
.main-nav a.nav-cta {
  background: var(--red); color: #fff; padding: 6px 12px; border-radius: 3px;
}
.main-nav a.nav-cta:hover { background: var(--red-dark); color: #fff; }

.head-search {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; color: #fff; border: 1px solid var(--line);
  border-radius: 6px; flex: 0 0 auto;
}
.head-search:hover { color: var(--red); text-decoration: none; }

.nav-toggle { display: none; }
/* Mobile: nav links live behind the hamburger (Kris, 2026-11-05) */
@media (max-width: 700px) {
  .nav-toggle {
    display: block; margin-left: auto; background: none; cursor: pointer;
    border: 1px solid var(--line); border-radius: 6px; color: #fff;
    font-size: 22px; line-height: 1; padding: 7px 13px;
  }
  .head-search { order: 2; margin-left: auto; }
  .nav-toggle { order: 3; margin-left: 0; }
  .main-nav { order: 4; display: none; flex-direction: column; align-items: stretch; gap: 0; width: 100%; margin-left: 0; }
  .main-nav.open { display: flex; }
  .main-nav a { padding: 12px 4px; border-top: 1px solid var(--line); }
  .main-nav a.nav-cta { text-align: center; margin: 8px 0 4px; }
}

.wrap { max-width: 980px; margin: 0 auto; padding: 18px 14px 110px; position: relative; z-index: 1; }

/* hero */
.hero { text-align: center; padding: 22px 0 8px; }
.hero-logo { max-width: 520px; width: 92%; }
/* Homepage composite hero (Kris 2026-11-06): the logo composites OVER
   the New collage banner as one banner-height unit at EVERY web width
   — desktop first, extended to mobile web the same day at Kris's word
   (the Android app is untouched) — and the strip inside #new is hidden
   at all widths (the banner layer replaces it). logo.png is
   black-backed RGB with no alpha, so the overlay uses
   mix-blend-mode: screen — black drops out against the collage,
   white/red art stays. The banner height is the .strip clamp, so
   phones sit at the 84px floor and wide screens cap at 200px; the
   logo rides at ~66% of the banner height, centered. */
.hero-composite { position: relative; padding: 0; }
/* Guest hero (Kris, 2026-11-07): his create-account banner replaces
   the New header + logo for logged-out visitors, linked to signup.
   Natural aspect (no cover-crop) so the banner's text never clips. */
.hero-guest { padding: 0; }
.hero-guest a { display: block; }
.hero-guest img { display: block; width: 100%; height: auto; border-radius: 10px; }
/* Guest conversion (Kris, 2026-11-10): the guest hero is real text —
   the one-line pitch with account buttons — plus the join prompt
   behind the Like button. (The after-3-plays "Enjoying the music?"
   join bar was removed altogether 2026-11-11 on Kris's order.)
   Red is the site's action color (.btn.like). */
.btn-join { background: var(--red); border-color: var(--red); color: #fff; }
.btn-join:hover { background: var(--red-dark); border-color: var(--red-dark); color: #fff; }
.hero-guest-text { padding: 26px 16px 18px; }
.guest-pitch { font-family: "Archivo Black", "Arial Black", Arial, sans-serif; font-size: 25px; line-height: 1.3; margin: 0 auto 10px; max-width: 660px; }
.guest-pitch .clock { color: var(--gold); }
.guest-sub { color: var(--dim); margin: 0 auto 16px; max-width: 580px; font-size: 15px; line-height: 1.5; }
.guest-cta { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.guest-cta .btn { padding: 10px 18px; font-size: 15px; }
.join-modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.72); display: flex; align-items: center; justify-content: center; z-index: 200; padding: 18px; }
.join-modal { background: var(--panel); border: 2px solid var(--gold); border-radius: 14px; max-width: 440px; width: 100%; padding: 22px; text-align: center; }
.join-modal h3 { margin: 0 0 8px; font-size: 20px; }
.join-modal p { color: var(--dim); margin: 0 0 16px; font-size: 14.5px; line-height: 1.5; }
.join-modal p strong { color: #fff; }
.join-modal .guest-cta { margin-bottom: 12px; }
.join-modal .jm-dismiss { background: none; border: 0; color: var(--dim); cursor: pointer; font-size: 13px; text-decoration: underline; }
.hero-composite .hero-banner { display: block; width: 100%; height: clamp(84px, 22vw, 200px); object-fit: cover; object-position: center; border-radius: 10px; }
/* All selected = no header art behind the logo (Kris, 2026-11-08):
   the banner keeps its box (size unchanged) but paints nothing. */
.hero-composite.no-art .hero-banner { visibility: hidden; }
.hero-composite picture { display: contents; }
.hero-composite .hero-logo { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: auto; max-width: 82%; height: 66%; mix-blend-mode: screen; pointer-events: none; }
/* #new's strip hidden here until 2026-11-10 (the hero banner
   carried its art); Kris restored it as the slim band that day —
   it follows the same rules as the genre bands now. */
/* While a type is selected its header art lives behind the logo
   only (Kris, 2026-11-08) — the section's own strip hides. */
section.strip-in-hero .strip { display: none; }
.hero-tag { color: var(--dim); max-width: 640px; margin: 12px auto 4px; font-size: 18px; }
.hero-tag strong { color: #fff; }
.hero-tag em { color: var(--red); font-style: normal; font-weight: 700; }

h1.page-title {
  font-size: 34px; text-transform: uppercase; letter-spacing: .04em;
  margin: 14px 0 2px; border-bottom: 2px solid var(--red); padding-bottom: 6px;
}
.page-sub { color: var(--dim); margin: 6px 0 16px; }
.sec-title {
  font-size: 23px; text-transform: uppercase; letter-spacing: .05em;
  margin: 30px 0 10px; border-left: 6px solid var(--red); padding-left: 10px;
}
.sec-title .sec-sub { font-size: 13px; color: var(--dim); text-transform: none; letter-spacing: 0; margin-left: 8px; }
.sec-sub { color: var(--dim); }
/* Between the homepage's charts: breathing room plus the site's
   standard hairline — the same 1px var(--line) as the footer's top
   border (Kris, 2026-11-06). Emitted by index.php after each chart's
   grid, never above the first section. */
/* Section dividers removed (Kris, 2026-11-10): the line under
   the pager arrows is gone altogether; the arrows are now the
   last thing in each homepage section. */

/* song rows */
.song-list { display: flex; flex-direction: column; gap: 8px; }
/* Card layout (round 5, 2026-11-06): the card is a CSS grid with named
   areas. song_row() already emits every part as a direct child of .song
   (head / clock / play / main / art / actions), so the art is a grid item
   at card level. ONE card design at every width now (Kris's mock): the
   40px art shares the title row at its top right, the timer row sits
   directly under the title with the likes at the right end (under the
   art), and the body row is play + byline/genre only — the old desktop
   body column with its 160px art and the tall dead void under the
   timer is gone. Rows for children a card doesn't have (Hall of Fame
   cards have no .song-clockrow; logged-out cards have no .song-actions)
   are dropped via :has() so an empty track can't leave a phantom
   row-gap behind. */
.song {
  display: grid; row-gap: 4px; column-gap: 10px;
  grid-template-columns: auto 1fr 40px;
  grid-template-areas:
    "head head art"
    "clock clock clock"
    "play main main"
    "actions actions actions";
  background: var(--panel); border: 1px solid var(--line); border-left: 4px solid var(--red);
  padding: 10px 12px; border-radius: 4px;
}
.song:not(:has(> .song-clockrow)) {
  grid-template-areas:
    "head head art"
    "play main main"
    "actions actions actions";
}
.song:not(:has(> .song-actions)) {
  grid-template-areas:
    "head head art"
    "clock clock clock"
    "play main main";
}
.song:not(:has(> .song-clockrow)):not(:has(> .song-actions)) {
  grid-template-areas:
    "head head art"
    "play main main";
}
.song-head { grid-area: head; }
.song-clockrow { grid-area: clock; }
.song > .play-btn { grid-area: play; align-self: center; }
.song > .song-text { grid-area: main; align-self: center; }
.song > .song-art, .song > .art-slot { grid-area: art; }
.song-actions { grid-area: actions; }
.song.hof { border-left-color: var(--gold); background: rgba(23,20,8,.5); }
.song.playing { outline: 2px solid var(--red); }
.play-btn {
  background: var(--red); color: #fff; border: 0; border-radius: 50%;
  width: 42px; height: 42px; font-size: 16px; cursor: pointer; flex: 0 0 auto;
}
.play-btn:hover { background: var(--red-dark); }
.song-text { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; }
.song-main { display: flex; flex-direction: column; min-width: 0; }
/* Title row at the top of the card (round 2, 2026-11-06): the 40px art
   shares this row at its right edge at every width (round 5).
   min-width:0 lets long titles wrap instead of stretching the grid. */
.song-head { flex: 1 1 100%; min-width: 0; }
/* Card titles are always ONE line, cut short with an ellipsis —
   a title that wraps to two lines stretches its card and its
   whole grid row, which made the gaps between cards uneven
   (Kris, 2026-11-10). The song page still shows the full title. */
.song-head .song-title { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.song-title { color: #fff; font-weight: 700; font-size: 18px; }
.song-artist { color: var(--dim); font-size: 14px; line-height: 1.45; }
.song-tags { display: flex; gap: 10px; align-items: center; font-size: 13px; }
.tag {
  background: var(--panel2); border: 1px solid var(--line); color: var(--txt);
  padding: 1px 8px; border-radius: 10px; text-transform: uppercase; font-size: 11px; letter-spacing: .05em;
}
/* Owner's genre pill on their song page: the pill itself is the tap
   target for the genre prompt — same pill, just a pointer and the
   site's red hover border as the affordance (Kris, 2026-11-06). */
.tag-genre-edit { cursor: pointer; }
.tag-genre-edit:hover { border-color: var(--red); color: #fff; text-decoration: none; }
.dur { color: var(--dim); }
.song-clock { text-align: left; margin-top: 4px; }
/* Hall of Fame compaction (Kris, 2026-11-06): the gold block in the
   text column strands "N likes" on its own line under the ★ HALL OF
   FAME row, inflating the card. Pull the likes up onto the gold-label
   line at every width — the phone treatment, now the desktop one too
   (the 560px block below repeats the same rule harmlessly). */
.song.hof .song-clock {
  margin-top: 0; display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
/* Timer row (round 3, 2026-11-06): full-width row directly under the
   title — countdown left, likes count at the right end. Non-Hall cards
   only; Hall cards keep .song-clock in the text column. */
.song-clockrow {
  flex: 1 1 100%; min-width: 0; display: flex; flex-direction: column;
  align-items: flex-start; justify-content: flex-start; gap: 0;
}
.song-clockrow .likes-count { line-height: 1.25; }
.song-clockrow .countdown { line-height: 1.2; }
.countdown {
  font-family: "Courier New", monospace; font-weight: 700; color: #fff; font-size: 17px;
}
.countdown.low { color: var(--red); }
.clock-label { color: var(--dim); font-size: 12px; display: block; }
.likes-count { color: var(--dim); font-size: 10px; display: block; } /* 25% smaller (Kris, 2026-11-08) */
.hof-badge { color: var(--gold); font-weight: 800; letter-spacing: .05em; }
.hof-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Playlist trash button: rides on the owner's playlist cards at
   every width — since 2026-11-06 it is the playlist page's only
   remove control (it replaced the "Remove a song" dropdown form;
   before that it rendered on phones only). */
.song .pl-trash-btn { display: inline-flex; align-items: center; justify-content: center; }
.pl-trash-btn svg { display: block; }
.hof-badge.big { font-size: 22px; }
.song-actions { display: flex; gap: 6px; align-items: center; flex-wrap: nowrap; flex: 1 1 100%; min-width: 0; order: 4; }
.song-actions .btn { white-space: nowrap; flex: 0 0 auto; padding: 7px 9px; height: 35px; line-height: 1.2; }
.song-actions .btn.share-btn { padding: 7px 10px; height: 35px; }
.song-actions select.add-pl {
  flex: 0 0 52px; width: 52px; height: 35px; padding: 7px 4px; text-align: center;
}
.mine { color: var(--dim); font-size: 13px; font-style: italic; }

/* buttons + forms */
.btn {
  background: var(--panel2); color: var(--txt); border: 1px solid var(--line);
  padding: 7px 12px; border-radius: 3px; cursor: pointer; font-size: 14px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; display: inline-block;
}
.btn:hover { border-color: var(--red); color: #fff; text-decoration: none; }
.btn.like { background: var(--red); border-color: var(--red); color: #fff; }
.btn.like:hover { background: var(--red-dark); }
/* Like buttons (cards + song page) are boxed at a FIXED width in both
   states — "♥ Support +24h" / "♥ Support" -> "Supported" — so flipping
   the label never resizes the button or shifts the actions row (Kris,
   2026-11-06; labels renamed 2026-11-12). Width = the widest label's
   rendered size measured in the real stylesheet (145px in the base
   context incl. padding/border) + a font-metric cushion for wider
   system fonts; text centered. Letter-spacing tightens to .01em on
   these buttons only — "♥ SUPPORT +24H" is wider than the old label
   and spilled at the old pins (Kris, 2026-11-12).
   Scoped by [data-like] so the Search submit (which reuses .btn.like
   purely for the red styling) keeps its natural width. */
.btn.like[data-like] { width: 156px; min-width: 156px; text-align: center; letter-spacing: .01em; }
.btn.dislike { color: var(--dim); }
.btn.cta { background: var(--red); color: #fff; border-color: var(--red); font-size: 16px; padding: 10px 18px; }
.btn.danger { background: var(--red-dark); color: #fff; border-color: var(--red); }
.btn.ghost { background: transparent; }
.btn.share-btn { display: inline-flex; align-items: center; justify-content: center; padding: 8px 12px; }
.btn.share-btn svg { width: 18px; height: 18px; display: block; }
.btn:disabled { opacity: .45; cursor: default; }
select.add-pl, select, input[type=text], input[type=email], input[type=password], input[type=file] {
  background: var(--panel2); color: var(--txt); border: 1px solid var(--line);
  padding: 7px 9px; border-radius: 3px; font-size: 15px;
}
.stack-form { display: flex; flex-direction: column; gap: 18px; max-width: 460px; }
.stack-form label { display: flex; flex-direction: column; gap: 5px; font-weight: 700; font-size: 14px; }
.stack-form label.check { flex-direction: row; align-items: flex-start; gap: 9px; font-weight: 400; font-size: 15px; margin-top: 2px; }
/* Form cleanup (Kris, 2026-11-08): stack-form controls get the
   calmer box — more padding, softer corners, one rhythm. */
.stack-form select, .stack-form input[type=text], .stack-form input[type=email], .stack-form input[type=password], .stack-form input[type=file] { padding: 10px 12px; border-radius: 8px; }
.stack-form .btn.cta { margin-top: 6px; }
.inline-form { display: flex; gap: 8px; margin: 12px 0; flex-wrap: wrap; }
.error { background: #3a0d10; border: 1px solid var(--red); padding: 10px 12px; border-radius: 4px; }
.hint { color: var(--dim); font-size: 14px; }
.empty { color: var(--dim); padding: 12px 4px; }

/* song page — one composed header panel (Kris, 2026-11-06): art +
   title/byline/stat in a top flex row, ONE action row beneath, one
   radio caption. The old stacked fragments (300px hero art on its own
   line, the full-width mostly-empty .big-clock bar, the play button
   alone on its line, the separate Cover art box duplicating the hero
   image) are gone; the owner's art controls now live on the header
   art itself (badge + prompt), like the genre pill pattern. */
.song-panel {
  background: var(--panel); border: 1px solid var(--line); border-left: 6px solid var(--red);
  border-radius: 4px; padding: 16px; margin-top: 14px;
}
.song-panel.hof { border-left-color: var(--gold); background: rgba(23,20,8,.5); }
.song-panel-top { display: flex; gap: 18px; align-items: flex-start; }
.song-panel-art { flex: 0 0 auto; width: 200px; }
.song-panel-img { display: block; width: 200px; height: 200px; object-fit: cover; border-radius: 10px; }
.song-art-btn {
  position: relative; display: block; width: 200px; background: none;
  border: 0; padding: 0; cursor: pointer;
}
.song-art-btn .song-panel-img { width: 100%; }
.art-badge {
  position: absolute; left: 8px; bottom: 8px; pointer-events: none;
  padding: 2px 9px; border-radius: 10px; font-size: 11px; font-weight: 800;
  text-transform: uppercase; letter-spacing: .05em;
  background: rgba(0,0,0,.78); color: #fff; border: 1px solid var(--line);
}
.art-badge.live { border-color: #4a7a2e; color: #bfe3a8; }
.art-badge.pending { border-color: var(--gold); color: var(--gold); }
.art-badge.rejected { border-color: var(--red); color: #ffb3b6; }
.art-badge.add { border-color: var(--red); }
.art-status-note { margin: 8px 0 0; font-size: 13px; }
.song-panel-main { flex: 1 1 auto; min-width: 0; }
.song-big-title { font-size: 40px; margin: 16px 0 2px; text-transform: uppercase; }
.song-panel .song-big-title { margin: 2px 0 4px; }
.song-big-artist { color: var(--dim); font-size: 18px; }
/* "Performed by:" credit line on the song page (Kris, 2026-11-10). */
.song-performed { color: var(--dim); font-size: 15px; margin: 4px 0 0; }
.card-performed { color: #fff; font-weight: 700; font-size: 10px; margin: 3px 0 0; }
/* Byline profile pictures (Kris, 2026-11-07): on cards a tiny square
   of the artist's picture REPLACES the word "by" — same height as the
   byline text (1em), square. On the song page the picture is ADDED at
   the left of the byline block, stretched to the block's height. */
.byline-ava { height: 1em; width: 1em; object-fit: cover; border-radius: 2px; vertical-align: -0.13em; margin-right: 4px; }
.song-byline-wrap { display: flex; align-items: stretch; gap: 10px; }
.song-byline-ava { font-size: 18px; height: 52px; width: 52px; height: 2lh; width: 2lh; flex: none; aspect-ratio: 1; object-fit: cover; border-radius: 6px; }
.song-byline-wrap .song-big-artist { align-self: center; }
.song-panel .song-big-artist { margin: 0; }
/* Compact stat line: ticking digits + likes as a labeled pair at a
   matching weight — no full-width bar, likes no longer stranded. */
.song-stat { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; margin-top: 12px; }
.song-stat .countdown.big { font-size: 38px; }
.stat-likes { color: var(--dim); font-weight: 700; font-size: 20px; white-space: nowrap; }
.song-page-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 16px; }
.song-page-actions .big-player { display: contents; }
.play-big { font-size: 18px; padding: 12px 22px; background: var(--red); color: #fff; border-color: var(--red); }
/* Song page actions matched to the app (Kris, 2026-11-07): the Play
   bar spans the full width, then ONE compact row — Like / Used /
   SUB / + / share / Report — even heights and short labels. */
.song-page-actions .play-big { flex: 1 1 100%; font-size: 16px; padding: 13px 10px; text-align: center; }
.song-page-actions { gap: 6px; }
.song-page-actions .btn { font-size: 12.5px; padding: 9px 2px; white-space: nowrap; text-align: center; }
.song-page-actions .btn.like[data-like] { width: auto; min-width: 0; }
.song-page-actions .btn.like, .song-page-actions .btn.dislike,
.song-page-actions [data-subscribe], .song-page-actions .btn.ghost { flex: 1 1 0; min-width: 0; }
.song-page-actions select.add-pl { flex: 0 0 38px; width: 38px; padding: 9px 0; text-align: center; font-size: 15px; appearance: none; -webkit-appearance: none; cursor: pointer; }
.song-page-actions .btn.share-btn { flex: 0 0 38px; }
/* Match the app's exact order: share before Report. */
.song-page-actions .btn.share-btn { order: 6; }
.song-page-actions [data-report] { order: 7; }
.song-radio-note { color: var(--dim); font-size: 14px; margin: 10px 0 0; }

/* playlists */
.pl-list { list-style: none; padding: 0; }
.pl-list li { padding: 8px 4px; border-bottom: 1px solid var(--line); font-size: 18px; }
.pl-controls { display: flex; gap: 8px; align-items: center; flex-wrap: nowrap; margin: 12px 0; }
.pl-controls .btn { flex: 1 1 0; padding-left: 4px; padding-right: 4px; white-space: nowrap; text-align: center; }
.mode-btn.active { background: var(--red); color: #fff; border-color: var(--red); }
.danger-zone { margin-top: 40px; border-top: 1px solid var(--line); padding-top: 14px; }

/* legal */
.legal h3 { color: #fff; text-transform: uppercase; letter-spacing: .04em; margin-top: 22px; }
.legal p { color: var(--dim); max-width: 68ch; }
.legal strong { color: #fff; }

/* footer + player bar */
.site-foot {
  border-top: 1px solid var(--line); padding: 16px; color: var(--dim);
  display: flex; gap: 16px; flex-wrap: wrap; justify-content: center; font-size: 14px;
  position: relative; z-index: 1;
}
/* Tesla coil visualizer backdrop (Kris, 2026-11-12): fixed canvas
   at z-index 0 — behind the lifted .wrap/.site-foot content, above
   the black page background. Never takes clicks. When app.js is
   not drawing (no music), it is transparent: the plain black site. */
#viz { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 0; pointer-events: none; }
/* Player-bar visualizer toggle: the bolt is full-color when the
   coil is live, grayed and dim when it is off. */
.pb-viz { background: none; border: 0; font-size: 16px; line-height: 1; cursor: pointer; padding: 4px 2px; filter: grayscale(1); opacity: .45; }
.pb-viz.on { filter: none; opacity: 1; }
.playerbar[hidden] { display: none; }
.playerbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  background: #0a0a0a; border-top: 3px solid var(--red);
  display: flex; align-items: center; gap: 14px; padding: 8px 14px; flex-wrap: wrap;
}
.playerbar audio { display: none; }
/* Custom player controls (2026-11-06): the native audio widget could
   not be themed (light-gray pill in the black/red bar), so the bar
   carries its own controls in the site's design language — red
   circular .play-btn toggle, dark var(--line) seek track with a red
   fill and white knob, dim time text, red-thumbed volume slider. */
.player-controls {
  flex: 1 1 260px; display: flex; align-items: center; gap: 10px; min-width: 0;
}
.pb-toggle { line-height: 1; }
/* Resume-after-refresh (Kris, 2026-11-12): when the browser
   blocked the automatic restart, the play button breathes a gold
   ring — "press me, your song is right where you left it" — until
   the first tap or keypress resumes playback. */
#pb-toggle.resume-pulse { animation: pb-resume-pulse 1.6s ease-in-out infinite; }
@keyframes pb-resume-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(246, 200, 76, 0); }
  50% { box-shadow: 0 0 0 7px rgba(246, 200, 76, .35); }
}
@media (prefers-reduced-motion: reduce) {
  #pb-toggle.resume-pulse { animation: none; outline: 2px solid var(--gold); }
}
.pb-time {
  color: var(--dim); font-size: 13px; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.pb-seek {
  flex: 0 0 auto; width: 100%; height: 6px; border-radius: 3px;
  background: var(--line); position: relative; cursor: pointer;
}
.pb-seek::before { content: ''; position: absolute; left: 0; right: 0; top: -7px; bottom: -7px; }
.pb-seek-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 0;
  background: var(--red); border-radius: 3px; pointer-events: none;
}
.pb-seek-knob {
  position: absolute; top: 50%; left: 0; width: 13px; height: 13px;
  border-radius: 50%; background: #fff; border: 2px solid var(--red);
  transform: translate(-50%, -50%); pointer-events: none;
}
.pb-mute {
  background: none; border: 0; color: var(--txt); cursor: pointer;
  font-size: 16px; line-height: 1; padding: 4px 2px; flex: 0 0 auto;
}
.pb-vol {
  flex: 0 0 84px; width: 84px; height: 4px; border-radius: 2px;
  -webkit-appearance: none; appearance: none;
  background: var(--line); outline: none; cursor: pointer; padding: 0; border: 0;
}
.pb-vol::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 12px; height: 12px;
  border-radius: 50%; background: var(--red); border: 0; cursor: pointer;
}
.pb-vol::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--red); border: 0; cursor: pointer;
}
/* Player-bar add-to-playlist "+" (Kris, 2026-11-09): the song cards'
   .add-pl select, sized to sit with the bar controls; deliberately
   NOT hidden by the phone media query below — adding the playing
   song is a phone-first action. */
.pb-addpl {
  flex: 0 0 38px; width: 38px; height: 38px; padding: 0;
  text-align: center; font-size: 17px; cursor: pointer;
  appearance: none; -webkit-appearance: none;
}
/* Remove Ads badge (Kris, 2026-11-11): left of the play toggle at the
   toggle's exact 42px height. Unpaid states get Kris's no-ads badge
   swinging ±45° around upright (2.5s each way, soft easing, always
   forward facing); Plus members get the circular Plus logo, static,
   in the same slot. The seek bar flexes to make the room. */
.pb-badge-btn {
  background: none; border: 0; padding: 0; cursor: pointer;
  flex: 0 0 auto; line-height: 0; display: block;
}
.pb-badge { width: 42px; height: 42px; display: block; flex: 0 0 auto; }
.pb-badge-swing {
  transform-origin: 50% 50%;
  animation: pb-swing 2.5s ease-in-out infinite alternate;
}
@keyframes pb-swing {
  from { transform: rotate(-45deg); }
  to { transform: rotate(45deg); }
}
/* Guest Sign In badge (Kris, 2026-11-11): pulses in and out like a
   heartbeat — two beats (lub-dub) then a rest, on a 1.8s cycle. */
.pb-badge-pulse {
  transform-origin: 50% 50%;
  animation: pb-heartbeat 1.8s ease-in-out infinite;
}
@keyframes pb-heartbeat {
  0%, 42%, 100% { transform: scale(1); }
  10% { transform: scale(1.14); }
  22% { transform: scale(1.02); }
  30% { transform: scale(1.10); }
}
@media (prefers-reduced-motion: reduce) {
  .pb-badge-swing, .pb-badge-pulse { animation: none; }
}
@media (max-width: 560px) {
  /* Player bar on phones: play + seek + times only — the volume
     slider and mute toggle hide (the OS owns volume on mobile) and
     the meta block narrows so the row stays on one line. */
  .pb-vol, .pb-mute { display: none; }
  .player-controls { flex-basis: 170px; gap: 8px; }
}
/* Player-bar meta (Kris, 2026-11-11): the song title and artist ride
   over the sliding bar — small type stacked inside the seek column,
   title white (capped at 25 characters in app.js), artist gray —
   instead of owning a separate column left of the controls. The
   block is click-transparent so the seek's extended hit area works
   through it. With the meta out of its column, the bar's flexible
   room belongs to the seekwrap at every width. */
.pb-seekwrap {
  flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column;
  justify-content: center; gap: 3px;
}
.player-meta { min-width: 0; line-height: 1.2; pointer-events: none; }
.player-meta strong {
  display: block; color: #fff; font-size: 12px; font-weight: 700;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.player-meta span {
  display: block; color: var(--dim); font-size: 11px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.toast {
  position: fixed; bottom: 84px; left: 50%; transform: translateX(-50%);
  background: var(--panel2); border: 1px solid var(--red); color: #fff;
  padding: 9px 16px; border-radius: 4px; z-index: 60; font-weight: 700;
}

@media (max-width: 560px) {
  .song-big-title { font-size: 24px; line-height: 1.15; }
  /* Title + byline move above the cover art (Kris, 2026-11-07): the
     main column dissolves (display:contents) so the panel stack can
     order title, byline, art, stats. The title is one line with an
     ellipsis; the byline keeps its full line. */
  .song-panel-main { display: contents; }
  .song-panel .song-big-title { order: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; width: 100%; }
  .song-byline-wrap { order: 2; min-width: 0; max-width: 100%; }
  .song-big-artist { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 14.5px; }
  .song-byline-wrap { gap: 8px; }
  .song-byline-ava { width: 38px; height: 38px; }
  /* Genre + length stack under the name, right of the avatar (Kris, 2026-11-07). */
  .song-big-artist { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; }
  .byline-name { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .byline-meta .sep-lead { display: none; }
  .song-panel-art { order: 3; }
  .song-stat { order: 4; }
  /* Mobile web only (Kris, 2026-11-07): the Play bar stays on one line. */
  .song-page-actions .play-big { white-space: nowrap; font-size: 14px; padding: 12px 6px; }
  .song-page-actions .btn { font-size: 11.5px; padding: 9px 1px; }
  /* One line, sized to fit (Kris, 2026-11-12): the Support labels
     outgrew the row's equal flex shares — "SUPPORTED" spilled over
     the Used button on phones. In this band the four text buttons
     size to their CONTENT (flex-basis auto) instead of splitting
     the row into equal quarters, the font steps down half a point,
     and the two fixed square buttons slim to 34px. The row stays
     on one line at every phone width. */
  .song-page-actions { gap: 5px; }
  .song-page-actions .btn { font-size: 10.5px; }
  .song-page-actions .btn.like, .song-page-actions .btn.dislike,
  .song-page-actions [data-subscribe], .song-page-actions .btn.ghost { flex: 1 1 auto; }
  .song-page-actions select.add-pl { flex-basis: 34px; width: 34px; }
  .song-page-actions .btn.share-btn { flex: 0 0 34px; }
  /* Song panel stacks (Kris, 2026-11-06): art at 160px above the text
     column; the action row wraps inside the panel; no horizontal
     scroll (panel padding + min-width:0 text column). */
  .song-panel { padding: 12px; }
  .song-panel-top { flex-direction: column; gap: 12px; }
  .song-panel-art, .song-art-btn { width: 160px; }
  .song-panel-img { width: 160px; height: 160px; }
  .song-art-btn .song-panel-img { width: 100%; }
  .song-stat .countdown.big { font-size: 28px; }
  .stat-likes { font-size: 17px; }
  .brand img { height: 34px; }
  .mature-badge { height: 34px; }
  /* Phone cards (Kris, 2026-11-06): the format is UNIFORM now — the
     mobile card takes the desktop panel composition at phone scale.
     The cover art leaves the title row's top-right corner and becomes
     a padded 84px square at the LEFT (110px on the 471px desktop card
     ≈ 23%, so ~84px on the 362px phone card), spanning the three
     right-column rows: title (single line, ellipsis) / byline / stat
     line (countdown digits with the likes beside them in gold). The
     play button leads a full-width action row under the block, with
     the actions box offset past it — the panel's arrangement exactly.
     Rows for a missing clockrow (Hall cards) drop via the same :has()
     ladder as the base rules; the act row always exists because the
     play button always sits in it. */
  .song {
    padding: 10px 12px; row-gap: 5px; column-gap: 10px;
    grid-template-columns: 84px 1fr;
    grid-template-rows: auto auto auto auto;
    grid-template-areas:
      "art head"
      "art main"
      "art clock"
      "act act";
  }
  .song:not(:has(> .song-clockrow)) {
    grid-template-rows: auto auto auto;
    grid-template-areas:
      "art head"
      "art main"
      "act act";
  }
  .song:not(:has(> .song-actions)) {
    grid-template-rows: auto auto auto auto;
    grid-template-areas:
      "art head"
      "art main"
      "art clock"
      "act act";
  }
  .song:not(:has(> .song-clockrow)):not(:has(> .song-actions)) {
    grid-template-rows: auto auto auto;
    grid-template-areas:
      "art head"
      "art main"
      "act act";
  }
  /* Placements mirroring the >=561px block: play leads the act row,
     the actions box is offset past it by margin so the two never
     overlay, and both sit centered in the row the play button sets. */
  .song > .play-btn { grid-area: act; justify-self: start; align-self: center; width: 40px; height: 40px; }
  .song-head { align-self: start; }
  .song > .song-text { align-self: start; }
  .song-actions { grid-area: act; align-self: center; margin-left: 48px; margin-top: 0; gap: 5px; }
  /* Byline on one wrapping row, as the panel renders it: artist,
     then the genre pill and duration after a middot. */
  .song-main { flex-direction: row; flex-wrap: wrap; align-items: center; column-gap: 6px; row-gap: 2px; }
    /* Stat line: the likes sit directly beside the digits, gold and
     bold — the panel pairing, replacing the old right-aligned dim
     likes at the far end. flex-wrap is a safety valve only: with a
     3-digit like count on a 360px screen the likes drop under the
     digits instead of pushing past the card edge. */
  .song-clockrow { justify-content: flex-start; gap: 0; flex-wrap: wrap; }
  .song-clockrow .likes-count, .song.hof .song-clock .likes-count { color: var(--dim); font-weight: 700; font-size: 9px; }
  .card-performed { font-size: 9px; }
  .song-title { line-height: 1.15; }
  /* Phone card titles (Kris, 2026-11-06): first line only — a long title
     truncates with an ellipsis inside the right column (the head grid
     area has min-width:0, so the ellipsis engages instead of
     stretching the track). Desktop titles may still wrap. */
  .song-head .song-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .song-artist { line-height: 1.3; }
  /* Action controls step down one size on phones so the full row —
     Play + Like + -24h + playlist + share — holds ONE line at 390px
     (the <=375px band below steps down once more so it also holds at
     360px). The Like pin is re-measured for this context: at 13px
     with 7px side padding the widest label ("♥ Support +24h") renders
     ~121px, so the box is pinned at 124px — identical in both states,
     so the flip never shifts the row. The pin lives on the card
     actions only; the song page keeps the desktop 144px box. */
  .song-actions .btn { font-size: 13px; padding: 7px 7px; }
  .song-actions .btn.share-btn { padding: 7px 7px; }
  .song-actions .btn.like[data-like] { width: 136px; min-width: 136px; }
  .song-actions select.add-pl { flex-basis: 44px; width: 44px; padding: 7px 2px; }
  /* The art is the grid's left track now: a padded 84px square (the
     owner's .art-slot button keeps the tap-to-change prompt and its
     image fills the same square). The element-qualified selectors
     outrank the 40px height the base cover-art rules (later in this
     file) pin on card art at every width. */
  .song > img.song-art, .song > button.art-slot { width: 100%; height: 84px; margin-left: 0; }
  .song > img.song-art { object-fit: cover; }
  .song .art-slot .song-art { width: 100%; height: 100%; object-fit: cover; }
  /* Hall of Fame compaction (Kris, 2026-11-06): on phones the gold block
     in the text column used to strand "N likes" on its own line under
     the ★ HALL OF FAME row, inflating the card. Pull the likes up onto
     the gold-label line and drop the block's top margin — the lines sit
     as tight as a regular card's. Desktop rendering is untouched. (The
     grid already reserves no clock track for HoF cards — the :has()
     variants above drop it; the dead air was inside this block.) */
  .song.hof .song-clock {
    margin-top: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  }
  /* The gold label steps down on phones so badge + likes
     share ONE line in the narrower right column (at desktop sizes
     they fit at full size). */
  .song .hof-badge { font-size: 15px; }
  /* Playlist page, phones: the per-card trash button (beside share)
     replaces the "Remove a song:" dropdown, which is desktop-only. */
  .song .pl-trash-btn { display: inline-flex; align-items: center; justify-content: center; }
  .song-actions .btn.pl-trash-btn { padding: 7px 7px; height: 35px; }
  #pl-remove-form { display: none; }
}

/* Narrow phones (<=375px, e.g. 360px wide): the uniform card's action
   row steps down once more — smaller play circle, 12px buttons, the
   Like box re-pinned at 116px for this size — so Play + Like + -24h
   + playlist + share (or the playlist page's trash) still hold ONE
   line instead of wrapping or pushing past the card edge. The
   composition is unchanged; only the controls' size steps down. */
@media (max-width: 375px) {
  .song > .play-btn { width: 36px; height: 36px; }
  .song-actions { margin-left: 42px; gap: 4px; }
  .song-actions .btn { font-size: 12px; padding: 7px 6px; }
  .song-actions .btn.share-btn { padding: 7px 6px; }
  .song-actions .btn.like[data-like] { width: 124px; min-width: 124px; }
  .song-actions select.add-pl { flex-basis: 40px; width: 40px; }
  .song-actions .btn.pl-trash-btn { padding: 7px 6px; }
  /* The Hall line tightens once more so badge + likes stay
     on one line at 360px too. */
  .song .hof-badge { font-size: 13px; }
  .song.hof .song-clock { gap: 6px; }
  .song .hof-line { gap: 6px; }
}

/* Cover art (owner-uploaded) — a grid item in the card's "art" area:
   a 40px square in the title row's top-right corner at EVERY width
   (round 5 — one card design everywhere; the 560px block repeats the
   same sizes). Songs without approved art show the generic placeholder
   (assets/default-art.jpg) in the same .song-art box. On owner cards
   (profile/hall) the art — real or placeholder — is wrapped in an
   .art-slot button that opens the cover-art prompt. */
.song.hof { position: relative; }
.song-art {
  flex: 0 0 auto; width: min(27%, 160px); height: auto;
  margin-left: auto; align-self: flex-start;
  object-fit: contain; border-radius: 6px;
}
/* In song cards the art (or the owner's .art-slot button wrapping it) is
   a direct grid child in the "art" area: the track sets the size — a
   fixed 40px column at every width — so it fills its track and the old
   margin-left:auto positioning is retired. The height/crop rules that
   used to live only in the 560px block now live here too, so the card
   art is the same 40px cover-cropped square on desktop and phones.
   (Outside cards, e.g. the song page's .art-box, the base rules above
   still apply.) */
.song > .song-art, .song > .art-slot { width: 100%; margin-left: 0; }
.song > .song-art { height: 40px; object-fit: cover; }
.song > .art-slot { height: 40px; }
.song .art-slot .song-art { width: 100%; height: 100%; object-fit: cover; }
.art-slot {
  flex: 0 0 auto; width: min(27%, 160px); margin-left: auto; align-self: flex-start;
  background: none; border: 0; padding: 0; cursor: pointer;
}
.art-slot .song-art { width: 100%; margin: 0; display: block; }
.art-slot.empty {
  aspect-ratio: 1; border: 1px dashed var(--line); border-radius: 6px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  color: var(--dim); background: rgba(255,255,255,.02); text-align: center; padding: 6px;
}
.art-slot-note { font-size: 24px; line-height: 1; }
.art-slot-text { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.art-box .art-slot { margin-left: 0; width: 160px; }
.art-thumb-btn { background: none; border: 0; padding: 0; cursor: pointer; display: block; }
.art-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
.art-form label { color: var(--dim); font-size: 13px; }
.art-form input[type="file"] { color: var(--dim); font-size: 13px; max-width: 210px; }
.art-box { margin-top: 22px; }
/* Albums (Kris, 2026-11-08) */
.album-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 12px; margin: 8px 0 6px; }
.album-card { display: block; background: rgba(16,16,16,.5); border: 1px solid var(--line); border-radius: 10px; padding: 10px; text-decoration: none; }
.album-card img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 6px; display: block; }
.album-card-title { display: block; color: #fff; font-weight: 700; margin-top: 8px; }
.album-card-sub { display: block; color: var(--dim); font-size: 12.5px; margin-top: 2px; }
.album-row { display: flex; gap: 12px; align-items: center; padding: 9px 0; border-top: 1px solid var(--line); }
.album-row img { width: 54px; height: 54px; object-fit: cover; border-radius: 6px; flex: 0 0 auto; }
.album-row-main { flex: 1 1 auto; display: flex; flex-direction: column; gap: 1px; }
.album-art-big { width: 180px; height: 180px; object-fit: cover; border-radius: 10px; display: block; margin-bottom: 10px; }
.album-song-row { display: flex; gap: 9px; align-items: center; padding: 6px 0; cursor: pointer; }

.art-thumb { max-width: 140px; max-height: 140px; object-fit: contain; border-radius: 6px; display: block; margin: 8px 0; }

/* Genre header strips — slim section bands (Kris, 2026-11-10):
   the full-height banners (200px) won't scale as the sections
   fill with songs, so the strip is now a slim band (36px max — not much taller than the name text on it, Kris)
   with the section's name over the art. On the homepage the
   strip sits in a .strip-band wrapper carrying the name; the
   standalone genre and Hall pages use the bare .strip and slim
   down with it. The New section's band is the artwork that used
   to sit under the old All section (All wore new.jpg) — #new's
   strip shows again as this band. */
.strip-band { position: relative; margin: 0 0 14px; }
/* Hall of Fame page (Kris, 2026-11-10): genre sections sit
   flush, exactly like the landing page's, so every section
   header lands at the same height when the bar switches. */
.strip-band .strip { margin: 0; }
.strip-name {
  position: absolute; left: 14px; top: 50%; transform: translateY(-50%); pointer-events: none;
  font-family: "Archivo Black", "Arial Black", Arial, sans-serif;
  font-size: clamp(15px, 1.9vw, 22px); letter-spacing: .04em;
  text-transform: uppercase; color: #fff;
  text-shadow: 0 2px 10px rgba(0, 0, 0, .9), 0 0 3px rgba(0, 0, 0, .9);
}
.strip { display: block; width: 100%; height: clamp(26px, 3vw, 36px); object-fit: cover; object-position: center; border-radius: 10px; margin: 0 0 14px; }

/* Search — magnifier at the right of the New section header (home) and the
   search box / results page, styled in the black/red theme (Kris, 2026-11-06) */
.sec-head { display: flex; align-items: flex-start; gap: 10px; }
.sec-head .sec-title { flex: 1 1 auto; }
.search-btn {
  margin-top: 28px; flex: 0 0 auto; width: 38px; height: 38px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--panel2); color: #fff; border: 1px solid var(--line);
  border-radius: 50%; cursor: pointer;
}
.search-btn:hover { border-color: var(--red); color: var(--red); }
.search-box { display: flex; gap: 8px; margin: 0 0 14px; }
.search-box[hidden] { display: none; }
.search-box input[type=search] {
  flex: 1 1 auto; min-width: 0; background: var(--panel2); color: var(--txt);
  border: 1px solid var(--red); padding: 9px 12px; border-radius: 3px; font-size: 16px;
}
.search-box.page { margin: 14px 0 16px; }
/* Home section tools (Kris, 2026-11-07): the filter + search row
   sits ABOVE the sections so the controls stay reachable under
   every filter state; a collapse chip sits left of every Top 10 /
   Hall header, sections expanded by default. */
.home-tools { display: flex; align-items: center; gap: 8px; margin: 10px 0 2px; }
/* Genre filter bar (Kris, 2026-11-08): slanted red-outlined buttons
   replace the select on web at every width — the select stays in
   the DOM as the state holder the buttons drive. The bar rides over
   the header's bottom edge, its container at 50% transparency, and
   the header logo centers in the space above the bar. Per-width
   geometry lives in the two media blocks (the bar's height is the
   overlap, and the logo's center point moves with it). */
.home-tools {
  position: relative; z-index: 8; background: rgba(5, 5, 5, .5);
  border: 1px solid rgba(30, 30, 30, .5);
}
.home-tools #sec-filter { display: none; }
.genre-bar {
  display: flex; flex: 1 1 auto; align-items: center; gap: 8px;
  flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
  /* Side padding gives the end buttons' skew overhang (~9px at
     this height) room to paint inside the scroll box. */
  padding: 3px 12px;
}
.genre-bar::-webkit-scrollbar { display: none; }
.gbtn {
  transform: skewX(-14deg); flex: 0 0 auto; cursor: pointer;
  background: linear-gradient(180deg, #1b1b1b, #060606);
  border: 2px solid var(--red); border-radius: 9px; padding: 9px 13px;
}
.gbtn span {
  display: inline-block; transform: skewX(14deg); color: #fff;
  font-family: "Archivo Black", "Arial Black", Arial, sans-serif;
  font-size: 13px; letter-spacing: .02em; white-space: nowrap;
}
.gbtn:hover { border-color: #ff6b6b; }
.gbtn.active { background: linear-gradient(180deg, #e02323, #8f0f0f); border-color: #ff7a7a; }
.home-tools .search-btn { background: #111; border-color: #3a3a3a; color: #fff; }
@media (min-width: 1024px) {
  /* One row, no scrolling (Kris, 2026-11-08): on desktop all 14
     genre buttons fit the bar's fixed ~922px — tighter padding
     and gaps and a 12px label buy back the ~195px the natural
     sizes overflowed by. Measured: zero overflow at 1024–1920px
     widths, the All button fully inside the bar. Below 1024px
     the bar keeps its scroll tray. */
  .genre-bar { overflow-x: visible; gap: 6px; padding: 3px 9px; }
  /* Fill the bar (Kris, 2026-11-10): buttons grow by equal amounts
     from their natural sizes to fill the bar left-to-right at every
     desktop width, so the row tracks the layout as it widens from
     the 2-column band to the 3-column band instead of leaving a
     black tail after "All". Where there is no extra space (the
     1024-1240 band) the buttons sit at their natural sizes, exactly
     as measured before. Mobile keeps the scroll tray untouched. */
  .gbtn { padding: 9px 8px; flex: 1 1 auto; text-align: center; }
  .gbtn span { font-size: 12px; }
  /* Overlap 104px (Kris, 2026-11-10): the gap from the header
     logo's artwork to this bar halved — the logo art fills only
     the middle ~47% of its black-backed canvas, so the visible
     gap measured ~31px at the old 73px overlap; riding the bar
     31px higher (with the logo recentering in step below) takes
     it to ~16px. */
  .home-tools { padding: 14px; margin: -104px 0 8px; gap: 10px; }
  .hero-composite .hero-logo { top: calc((100% - 104px) / 2); }
}
@media (max-width: 1023px) {
  .home-tools { padding: 10px; margin: -88px 0 8px; gap: 8px; }
  /* A taller stage on phones so the logo has a real space above
     the bar to center in. */
  .hero-composite .hero-banner { height: 150px; }
  .hero-composite .hero-logo { top: calc((100% - 88px) / 2); height: 54%; }
}
/* Guests (Kris, 2026-11-08): logged out only, the genre bar sits
   fully UNDER the create-account banner instead of overlapping it.
   Members keep the overlapping bar exactly as designed. */
.home-tools.guest-tools { margin-top: 10px; }
/* Guest hero in the logo's space (Kris, 2026-11-10): the
   account-creation pitch used to stack tall and push the genre
   bar down the page (the 2026-11-08 underlap). At desktop
   widths it now takes the member logo hero's exact box — same
   height, same bar overlap — with the copy laid out beside the
   buttons, centered in the visible zone above the bar, so the
   navigation bar sits in the identical place logged out as
   logged in. Phones keep the stacked layout above: the pitch
   can't fit the logo's space at a readable size there. */
@media (min-width: 1024px) {
  .hero-guest-text {
    height: clamp(84px, 22vw, 200px); box-sizing: border-box;
    display: flex; flex-direction: row; align-items: center; justify-content: center;
    gap: 34px; padding: 0 16px 104px; text-align: left;
  }
  .guest-copy { max-width: 630px; }
  .hero-guest-text .guest-pitch { font-size: 23px; line-height: 1.25; margin: 0 0 6px; max-width: none; }
  .hero-guest-text .guest-sub { font-size: 13.5px; line-height: 1.42; margin: 0; max-width: none; }
  .hero-guest-text .guest-cta { flex: 0 0 auto; flex-wrap: nowrap; }
  .home-tools.guest-tools { margin: -104px 0 8px; }
}
@media (min-width: 1420px) {
  /* Room for the longer pitch sub (Kris, 2026-11-10): the copy
     column widens so the sub keeps its two lines. */
  .hero-guest-text .guest-copy { max-width: 690px; }
}
/* Guest hero fits the member hero's box on phones too (Kris,
   2026-11-11): the stacked pitch used to push the genre bar far
   below its logged-in position. With the sub copy removed (Kris,
   same evening) the pitch and the two account buttons ride in the
   member banner's exact geometry — a 150px box with the bar
   overlapping its bottom 88px — so the navigation bar lands in
   the identical place logged in or out. */
@media (max-width: 1023px) {
  .hero-guest-text {
    height: 150px; box-sizing: border-box;
    display: flex; flex-direction: row; align-items: center;
    gap: 14px; padding: 0 14px 88px; text-align: left;
  }
  .hero-guest-text .guest-copy { flex: 1 1 auto; min-width: 0; }
  .hero-guest-text .guest-pitch { font-size: 16.5px; line-height: 1.18; margin: 0; max-width: none; }
  .hero-guest-text .guest-cta { flex: 0 0 auto; flex-direction: column; flex-wrap: nowrap; gap: 4px; }
  .hero-guest-text .guest-cta .btn { padding: 5px 10px; font-size: 11.5px; white-space: nowrap; }
  .home-tools.guest-tools { margin: -88px 0 8px; }
}
@media (max-width: 480px) {
  /* Narrow phones: the row runs out of width, so the buttons drop
     to a slim row under the pitch — still inside the same box. */
  .hero-guest-text { flex-direction: column; justify-content: center; gap: 4px; padding: 0 12px 88px; text-align: center; }
  .hero-guest-text .guest-pitch { font-size: 14px; line-height: 1.12; }
  .hero-guest-text .guest-cta { flex-direction: row; gap: 8px; }
  .hero-guest-text .guest-cta .btn { padding: 4px 10px; font-size: 11px; }
}
.home-tools .search-btn { margin-top: 0; margin-left: auto; }
#sec-filter { height: 38px; max-width: 62%; }
.sec-collapse {
  flex: 0 0 auto; width: 32px; height: 32px; margin-top: 28px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--panel2); color: #fff; border: 1px solid var(--line);
  border-radius: 8px; cursor: pointer;
}
.sec-collapse:hover { border-color: var(--red); color: var(--red); }
.sec-collapse svg { transition: transform .15s ease; }
section.is-collapsed .sec-collapse svg { transform: rotate(-90deg); }
section.is-collapsed .song-list, section.is-collapsed .rail-sec-title { display: none; }
section.filtered-out { display: none; }

/* IndieTime+ / admin (2026-11-06) */
/* Plus member badge art (Kris's black seal) right of names — scales with text */
.plus-badge { display: inline-block; height: 1em; width: 1em; min-width: 1em; flex: none; object-fit: contain; vertical-align: -0.12em; margin-left: .3em; margin-right: 0; }
/* Same seal as product branding, left of the IndieTime+ card heading */
.plus-badge.head { margin-left: 0; margin-right: .3em; }
.plus-panel { background: var(--panel); border: 1px solid var(--line); border-left: 4px solid var(--red); border-radius: 4px; padding: 14px; max-width: 560px; }
.plus-status { margin: 0 0 8px; }
.plus-buy { display: flex; gap: 10px; flex-wrap: wrap; margin: 10px 0; }
.plus-note { background: #14250f; border: 1px solid #4a7a2e; padding: 10px 12px; border-radius: 4px; max-width: 560px; }
.admin-card { background: var(--panel); border: 1px solid var(--line); border-left: 4px solid var(--red); border-radius: 4px; padding: 14px; }
.auto-tag { display: inline-block; padding: 0 7px; border: 1px solid var(--line); border-radius: 9px; font-size: 12px; color: #f5c542; vertical-align: middle; }
.admin-card h3 { color: #fff; text-transform: uppercase; letter-spacing: .04em; margin: 22px 0 8px; }
input[type=number], input[type=url] {
  background: var(--panel2); color: var(--txt); border: 1px solid var(--line);
  padding: 7px 9px; border-radius: 3px; font-size: 15px;
}
.player-ad-note { color: var(--gold); font-weight: 700; text-transform: uppercase; letter-spacing: .05em; font-size: 12px; }

/* Hall of Fame earnings (2026-11-06) — horizontal payout-goal bar */
.earnings-card { background: var(--panel); border: 1px solid var(--line); border-left: 4px solid var(--gold); border-radius: 4px; padding: 14px; max-width: 560px; }
.earn-amounts { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.earn-now { color: #fff; font-weight: 800; font-size: 20px; }
.earn-goal { color: var(--dim); font-weight: 700; }
.earn-bar {
  height: 20px; background: #000; border: 1px solid var(--line);
  border-right: 3px solid var(--gold); border-radius: 10px;
  margin: 12px 0 4px; overflow: hidden;
}
.earn-bar.ready { border-color: var(--gold); border-right: 3px solid var(--gold); }
.earn-fill { height: 100%; background: linear-gradient(90deg, var(--red-dark), var(--red)); transition: width .6s ease; }
.earn-scale { display: flex; justify-content: space-between; color: var(--dim); font-size: 12px; }
.earn-ready { color: var(--gold); font-weight: 800; margin: 10px 0 0; }
.earn-deadline { color: #fff; background: #3a0d10; border: 1px solid var(--red); padding: 9px 11px; border-radius: 4px; font-weight: 700; margin: 10px 0 0; }

/* Hall of Fame celebration modal (2026-11-06) — the owner's popup when a
   song is inducted: share card, congratulations, canvas fireworks. */
.hof-celebration { position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.88); padding: 18px; }
.hof-celebration[hidden] { display: none; }
.hof-celebration .hof-fw { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.hof-celebration-card { position: relative; background: var(--panel); border: 2px solid var(--gold); border-radius: 14px; max-width: 640px; width: 100%; padding: 22px; text-align: center; box-shadow: 0 0 60px rgba(245,197,66,.25); max-height: 92vh; overflow: auto; }
.hof-celebration-kicker { color: var(--gold); font-weight: 800; letter-spacing: .12em; text-transform: uppercase; font-size: 13px; }
.hof-celebration-card h2 { margin: 6px 0 14px; font-size: 34px; }
.hof-celebration-img { width: 100%; height: auto; border-radius: 8px; border: 1px solid var(--line); background: #000; }
.hof-celebration-card p { color: var(--dim); font-size: 17px; line-height: 1.45; }
.hof-celebration-card p strong { color: var(--txt); }
.hof-celebration-btns { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 16px; }

/* Cover-art prompt — the review policy text lives here, in the prompt,
   not in the regular card view */
.art-prompt { position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.85); padding: 18px; }
.art-prompt[hidden] { display: none; }
.art-prompt-card { background: var(--panel); border: 1px solid var(--line); border-top: 4px solid var(--red); border-radius: 10px; max-width: 420px; width: 100%; padding: 22px; text-align: center; }
.art-prompt-card h2 { margin: 0 0 10px; font-size: 24px; }
.art-prompt-policy { color: var(--dim); font-size: 15px; line-height: 1.45; margin: 0; }
.art-prompt-btns { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 18px; }
/* "Are you still there?" presence prompt (2026-11-06): the cover-art
   prompt's dialog language, cloned (.art-prompt / .art-prompt-card).
   Rendered by layout_foot with the playerbar, outside the soft-nav
   swap region; z-index sits above the celebration overlay. */
.still-there { position: fixed; inset: 0; z-index: 210; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.85); padding: 18px; }
.still-there[hidden] { display: none; }
.still-there-card { background: var(--panel); border: 1px solid var(--line); border-top: 4px solid var(--red); border-radius: 10px; max-width: 420px; width: 100%; padding: 22px; text-align: center; }
.still-there-card h2 { margin: 0 0 10px; font-size: 24px; }
.still-there-btns { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 18px; }
/* Remove Ads card (Kris, 2026-11-11): overlay in the still-there
   dialog language, but the card itself is Kris's art on black — the
   header/benefits image with his three button images as real links
   stacked beneath it (monthly + annual side by side, trial wide). */
.remove-ads { position: fixed; inset: 0; z-index: 210; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.85); padding: 18px; overflow-y: auto; }
.remove-ads[hidden] { display: none; }
.ra-card { position: relative; background: rgba(0,0,0,.5); border: 1px solid var(--line); border-top: 4px solid var(--red); border-radius: 10px; max-width: 400px; width: 100%; padding: 12px 12px 16px; margin: auto; }
.ra-top { display: block; width: 100%; height: auto; }
.ra-btn-row { display: flex; gap: 10px; margin-top: 4px; }
.ra-btn { flex: 1 1 0; display: block; line-height: 0; }
.ra-btn img { width: 100%; height: auto; display: block; }
.ra-btn-wide { margin-top: 10px; }
.ra-close {
  position: absolute; top: -12px; right: -12px; z-index: 2;
  width: 30px; height: 30px; border-radius: 50%;
  background: #161616; border: 1px solid var(--line); color: #fff;
  font-size: 14px; line-height: 1; cursor: pointer; padding: 0;
}

/* Genre prompt (2026-11-06) — rides the cover-art prompt's shell above
   (.art-prompt / .art-prompt-card); only its own inner elements need
   rules: the canonical-genre picker and an inline error line. */
.genre-prompt-select { width: 100%; margin-top: 14px; }
.genre-prompt-msg { color: var(--red); font-size: 14px; margin: 10px 0 0; }

/* Email verification (2026-11-06) — profile banner for unverified accounts */
.verify-banner { background: #3a0d10; border: 1px solid var(--red); border-left: 4px solid var(--red); border-radius: 4px; padding: 12px 14px; max-width: 560px; margin: 0 0 18px; }
.verify-banner p { margin: 0 0 10px; }

/* Desktop list cards v2 (Kris, 2026-11-06): the card takes the SONG
   PAGE PANEL's composition at card scale — a padded square of cover
   art at the left (the panel's 200px art at its ~920px inner width
   scales to ~110px at the half-width card, keeping the panel's
   art:text proportions), then title / byline / stat line stacked
   beside it, and ONE action row spanning the card's full width under
   the art+text block. The stat line pairs the countdown digits with
   the likes count beside them in gold (the panel's .song-stat
   treatment) — likes are no longer a separate line. Lists lay out
   TWO CARDS PER ROW once two cards can sit side by side without
   crushing the text column (>=861px); from 561-860px the same
   composition runs full-width, one per row. Phones (<=560px) keep
   the round-5 mini-card exactly as-is — nothing in this block
   applies there. */
@media (min-width: 561px) {
  /* The area templates are restated through the same :has() variant
     ladder as the base card rules — the variants outrank a plain
     .song selector, so each rung must be answered at equal
     specificity. The composition itself only distinguishes the
     timer row (Hall cards have none); the "act" row exists either
     way because the play button always sits in it. */
  .song {
    grid-template-columns: 110px 1fr;
    grid-template-rows: auto auto auto auto;
    row-gap: 6px; column-gap: 12px;
    padding: 12px;
    grid-template-areas:
      "art head"
      "art main"
      "art clock"
      "act act";
  }
  .song:not(:has(> .song-clockrow)) {
    grid-template-rows: auto auto auto;
    grid-template-areas:
      "art head"
      "art main"
      "act act";
  }
  .song:not(:has(> .song-actions)) {
    grid-template-rows: auto auto auto auto;
    grid-template-areas:
      "art head"
      "art main"
      "art clock"
      "act act";
  }
  .song:not(:has(> .song-clockrow)):not(:has(> .song-actions)) {
    grid-template-rows: auto auto auto;
    grid-template-areas:
      "art head"
      "art main"
      "act act";
  }
  /* Art: the panel's padded square — a grid item in the left track,
     full track width, square, top-aligned (NOT a full-height bleed
     column). The owner's .art-slot button keeps the tap-to-change
     prompt; its image fills the same square. */
  .song > .song-art, .song > .art-slot {
    grid-area: art; width: 110px; height: 110px;
    margin-left: 0; align-self: start;
  }
  .song > .song-art { object-fit: cover; }
  .song .art-slot .song-art { width: 100%; height: 100%; object-fit: cover; display: block; }
  .song-head { grid-area: head; align-self: start; }
  .song > .song-text { grid-area: main; align-self: start; }
  /* Byline on one line, as the panel renders it: artist, then the
     genre pill and duration after a middot. */
  .song-main { flex-direction: row; flex-wrap: wrap; align-items: center; column-gap: 6px; row-gap: 2px; }
    /* Stat line: countdown left, likes directly beside it in gold at a
     matching bold weight — the panel's stat pairing. */
  .song-clockrow { grid-area: clock; justify-content: flex-start; gap: 14px; margin-top: 0; }
  .song .countdown { font-size: 22px; }
  .song-clockrow .likes-count, .song.hof .song-clock .likes-count { color: var(--dim); font-weight: 700; font-size: 12px; }
  /* Action row: full card width under the block. The play button and
     .song-actions share the "act" area; the actions box is offset by
     margin (not padding) so its box never overlays the play button —
     Play leads the row exactly as on the song page, then the card's
     usual controls (Like / -24h / playlist + / share; owner variants
     as rendered). */
  .song > .play-btn { grid-area: act; justify-self: start; align-self: center; }
  .song-actions { grid-area: act; align-self: center; margin-left: 54px; flex-wrap: wrap; }
}
@media (min-width: 861px) {
  .song-list { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .song-list > .empty { grid-column: 1 / -1; }
}
/* Wide desktop (Kris, 2026-11-10): when there is room to fill, pages
   built on song lists widen past the 980px column (up to 1440px) and
   the music goes 3-across instead of leaving black margins. Rail
   pages (.has-cols) are excluded — their main column keeps its 952px
   / 2-across design and the rail is what fills the right side. */
@media (min-width: 1240px) {
  .wrap:not(.has-cols):has(.song-list) { max-width: 1440px; }
  .wrap:not(.has-cols) .song-list { grid-template-columns: 1fr 1fr 1fr; }
}

/* Hall of Fame rail (Kris, 2026-11-06; compact format per Kris's
   same-day correction — the first cut's large stacked cards towered
   past the viewport, and he wants ALL TEN slots visible from the main
   screen): below the rail breakpoint the columns wrapper is inert and
   the rail does not render — every page is exactly the centered column
   it was. At >=1420px the frame widens and left-anchors: the main
   column keeps its 952px content width and its 2-across grids, and
   the rail fills the space to the right (cards capped at 400px,
   hugging the frame's right edge). The footer already lives outside
   <main> and stays full-width.
   HERO PLACEMENT (Kris's correction, 2026-11-06): there is NO
   masthead above the columns — the homepage composite hero is the
   first element INSIDE the main column, at main-column width,
   directly above the NEW header, and the rail starts at the very
   top of the frame, pushed up close to the site header's red line.
   STICKY (Kris, 2026-11-06): at >=1420px the rail is
   position:sticky under the (itself sticky) site header, capped at
   the viewport height with its own internal scroll, so the
   showcase stays in view while the main column scrolls and every
   slot stays reachable.
   TWO COLUMNS (Kris, 2026-11-06): at >=1500px the sections sit
   side by side — Top 10 left, Discover the Hall right — as FLEXIBLE
   columns: each section is (rail width - 24px gap) / 2, flexing
   from 228px at the breakpoint up to the 400px cap where the rail
   reaches its 824px max. Ten slots per section (Kris: "Make each
   10 instead of 5") — twenty slots total; in the two-column band
   the height-keyed fit steps below keep all twenty plus headers
   inside the first screen at 1080px-class heights.
   Rail cards are COMPACT ROWS: a ~76px square of art at the left,
   then title (one line, ellipsis) / byline / gold likes line / one
   compact action row stacked at the right. */
.col-rail { display: none; }
.hof-rail { display: flex; flex-direction: column; gap: 3px; }
/* Section wrapper: inert in the stacked band (identical flow and
   spacing); the two .rail-sec blocks become the side-by-side columns
   at >=1500px. */
.rail-sec { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.rail-head {
  font-size: 20px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--gold); margin: 0; line-height: 1.2;
}
/* Every left bar in the rail is the same dark gray #4d4d4d
   (Kris, 2026-11-10 — first the Featured / Discover headers and
   cards, then all of them: section titles, song cards, teasers,
   the Featured Album and Featured Artist cards). The .rail-sec-gray
   modifier is now redundant but kept for the markup that uses it. */
.rail-sec-title.rail-sec-gray { border-left-color: #4d4d4d; }
.rail-sec-title {
  font-size: 15px; text-transform: uppercase; letter-spacing: .05em; color: #fff;
  margin: 4px 0 0; border-left: 6px solid #4d4d4d; padding-left: 10px; line-height: 1.25;
}
.rail-card {
  display: flex; gap: 10px; align-items: center;
  background: rgba(23,20,8,.5); border: 1px solid var(--line); border-left: 4px solid #4d4d4d;
  border-radius: 4px; padding: 4px 8px;
}
.rail-art-link { display: block; flex: 0 0 auto; }
.rail-art { display: block; width: 74px; height: 74px; object-fit: cover; border-radius: 5px; }
.rail-body { flex: 1 1 auto; min-width: 0; }
.rail-song-title {
  display: block; color: #fff; font-weight: 700; font-size: 15px; line-height: 1.1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rail-byline {
  display: flex; align-items: center; gap: 7px; margin-top: 1px;
  font-size: 12px; line-height: 1.2; white-space: nowrap; overflow: hidden;
}
.rail-byline .song-artist { line-height: 1.2; }
.rail-byline .tag { padding: 0 5px; font-size: 10px; }
.rail-likes { display: flex; align-items: center; gap: 8px; margin-top: 1px; font-size: 12px; line-height: 1.2; }
.rail-likes .hof-badge { font-size: 12px; }
.rail-likes-n { color: var(--dim); font-weight: 700; }
.rail-actions { display: flex; gap: 5px; align-items: center; margin-top: 2px; }
.rail-actions .play-btn { width: 26px; height: 26px; font-size: 11px; }
.rail-actions .btn { font-size: 11.5px; padding: 3px 7px; white-space: nowrap; }
/* The rail's Like box is pinned narrower than the list cards' 144px —
   same two-state, no-shift rule at this context's size ("♥ Liked
   today" at 12px in the rail's padding ≈ 100px). */
.rail-actions .btn.like[data-like] { width: 106px; min-width: 106px; padding-left: 4px; padding-right: 4px; text-align: center; }
/* Featured Album (Kris, 2026-11-08): one large card over the
   five Discover slots. Sized by his alignment rule (revised
   2026-11-10): the BOTTOM of this card lines up with the bottom
   of the TOP 10 column's #5 card. Heights measured against the
   live rail (451 full / 370 compact / 320 mini, per fit step
   below) — supersedes the 2026-11-08 rule that aligned the first
   Discover card's top with #6's top. */
.rail-feature-card {
  display: flex; flex-direction: column; height: 451px;
  background: rgba(27,27,27,.5); border: 1px solid #3a3a3a; border-left: 4px solid #4d4d4d;
  border-radius: 4px; overflow: hidden; text-decoration: none;
}
.rail-feature-art { flex: 1 1 auto; min-height: 0; width: 100%; object-fit: cover; display: block; }
.rail-feature-body { display: block; padding: 10px 12px 12px; }
.rail-feature-title { display: block; color: #fff; font-weight: 800; font-size: 20px; line-height: 1.15; }
.rail-feature-by { display: block; color: var(--dim); font-size: 13px; margin-top: 2px; }
/* Featured Artist card (Kris, 2026-11-10): sits under the rail's
   Top 5 where slots #6-#10 used to be. Same shell family as the
   Featured Album card; its height is set per fit step (in the
   featured-heights blocks below) by his alignment rule: this
   card's bottom lines up with the bottom of the Discover Indie
   Music section across the rail. The art is the artist's avatar,
   flexed to fill like the album art. */
.rail-artist-card {
  display: flex; flex-direction: column; height: 450px;
  background: rgba(27,27,27,.5); border: 1px solid #3a3a3a; border-left: 4px solid #4d4d4d;
  border-radius: 4px; overflow: hidden;
}
.rail-artist-artlink { display: block; flex: 1 1 auto; min-height: 0; }
.rail-artist-art { width: 100%; height: 100%; object-fit: cover; display: block; }
.rail-artist-card .rail-feature-title a { color: #fff; text-decoration: none; }
.rail-artist-actions { display: block; margin-top: 10px; }
/* Album credit left of Likes on song cards (Kris, 2026-11-08). */
.album-link { color: #fff; font-weight: 700; text-decoration: none; }
.album-link:hover { text-decoration: underline; }
/* Rail identity tags (Kris, 2026-11-08): in the rail, a Hall of
   Fame song's genre tag is gold; every other song's is red. */
.rail-card .tag.hof-tag { background: #3a2f08; color: var(--gold); border: 1px solid var(--gold); }
.rail-card .tag.live-tag { background: #3d0a0e; color: #ff5b66; border: 1px solid var(--red); }
/* Section-top Featured Album (mobile web + app sections; the
   desktop rail carries its own, so this hides at >=1420px). */
/* Merged section header (Kris, 2026-11-08): the section's header
   banner is this card's background at 50% transparency — the
   --sec-strip layer under the content — and the standalone strip
   above the card hides wherever the card renders (below 1420px;
   at desktop widths the card itself hides and the strip stays,
   as the rail carries the desktop feature). Sections with no
   eligible featured album keep their standalone strip. */
.sec-feature {
  position: relative; overflow: hidden;
  display: flex; gap: 10px; align-items: center; margin: 8px 0;
  background: rgba(27,27,27,.5); border: 1px solid #3a3a3a; border-left: 4px solid #4d4d4d;
  border-radius: 4px; padding: 8px; text-decoration: none;
}
.sec-feature::before {
  content: ""; position: absolute; inset: 0;
  background-image: var(--sec-strip, none);
  background-size: cover; background-position: center;
  opacity: .5; pointer-events: none;
}
@media (max-width: 1419px) {
  section:has(> .sec-feature) > .strip-band { display: none; }
  section:has(> .sec-feature) > .strip { display: none; }
}
.sec-feature-art { position: relative; z-index: 1; width: 64px; height: 64px; object-fit: cover; border-radius: 4px; flex: 0 0 auto; display: block; }
.sec-feature-body { position: relative; z-index: 1; display: block; min-width: 0; text-shadow: 0 1px 4px rgba(0,0,0,.95), 0 0 2px rgba(0,0,0,.9); }
.sec-feature-eyebrow { display: block; color: #fff; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.sec-feature-title { display: block; color: #fff; font-weight: 800; font-size: 16px; line-height: 1.15; }
.sec-feature-by { display: block; color: var(--dim); font-size: 12px; margin-top: 1px; }
@media (min-width: 1420px) { .sec-feature { display: none; } }
.rail-card.playing { outline: 2px solid var(--red); }
/* Placeholder slot: the same compact shell, dimmed — the section rank
   as a numeral where the art would sit, the standing line beside it.
   No art, no buttons. min-height matches a real card's footprint. */
.rail-ph { opacity: .5; }
.rail-ph, .rail-teaser { min-height: 88px; }
.rail-ph-rank {
  flex: 0 0 76px; text-align: center;
  font-size: 34px; font-weight: 800; color: var(--dim); line-height: 1;
}
.rail-ph-text {
  color: var(--dim); font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; margin: 0; font-size: 13px;
}
/* Unfilled Top-10 teaser (Kris, 2026-11-07): golden trophy backdrop
   at 50% under the rank number and the earn-a-spot lines. */
.rail-teaser { position: relative; overflow: hidden; text-decoration: none; }
.rail-teaser-bg { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .5; pointer-events: none; }
.rail-teaser-rank { position: relative; color: var(--gold); text-shadow: 0 1px 8px rgba(0,0,0,.65); }
.rail-teaser-body { position: relative; min-width: 0; }
.rail-teaser-line1 { margin: 0; color: #fff; font-weight: 800; font-size: 14.5px; line-height: 1.15; text-shadow: 0 1px 6px rgba(0,0,0,.8); }
.rail-teaser-line2 { margin: 2px 0 0; color: var(--gold); font-weight: 700; font-size: 11.5px; line-height: 1.2; text-shadow: 0 1px 6px rgba(0,0,0,.8); }
.rail-teaser-c { justify-content: center; text-align: center; }
/* #1 HIT ribbon (Kris, 2026-11-07): the chart-topper bonus — the
   current #1 Hall song's cards wear a bright gold ribbon at their
   right edge. Deliberately more visible than a whisper. */
.song, .rail-card { position: relative; }
.song-panel { position: relative; }
.song.has-hit, .song-panel.has-hit { padding-right: 44px; }
.rail-card.has-hit, .rail-card.has-rank { padding-right: 40px; }
/* Mobile squeeze (Kris flag, 2026-11-07): on a narrow screen the
   ribbon's 44px inset starved the one nowrap actions row and the
   share button slid under the ribbon. On has-hit cards only, the
   row tightens — smaller gaps and button padding, a slimmer Like
   pin and playlist select — until every button clears the gold. */
@media (max-width: 560px) {
  .song.has-hit .song-actions { gap: 3px; }
  .song.has-hit .song-actions .btn { padding: 7px 5px; }
  .song.has-hit .song-actions .btn.like[data-like] { width: 112px; min-width: 112px; }
  .song.has-hit .song-actions select.add-pl { flex-basis: 36px; width: 36px; padding: 7px 2px; }
}
/* Gray rank banner: the #1 ribbon's quiet sibling for Top-10 slots
   2-10 — same strip, gray gradient, current rank only. */
.hit-ribbon.rank-ribbon { background: linear-gradient(180deg, #e6e6e6 0%, #9d9d9d 55%, #636363 100%); }
.hit-ribbon {
  position: absolute; top: 0; right: 0; bottom: 0; width: 30px; z-index: 2;
  display: flex; align-items: center; justify-content: center;
  background: linear-gradient(180deg, #ffe066 0%, var(--gold) 55%, #9a7410 100%);
  color: #141414; font-weight: 800; font-size: 12.5px; letter-spacing: .14em;
  writing-mode: vertical-rl; transform: rotate(180deg);
  border-radius: 4px 0 0 4px; box-shadow: 2px 0 10px rgba(0,0,0,.5);
}
/* Hall of Fame brand header (Kris, 2026-11-06): the desktop rail
   head's title — the words "Hall of Fame" set as TEXT in the
   IndieTime wordmark's own lettering. The logo's letters are a
   single black weight of lowercase grotesque (round i-dots, tight
   spacing, flat terminals); Archivo Black (SIL Open Font License,
   self-hosted) is its near-twin, so the header renders in it —
   lowercase like the mark — in gold, centered. Sizing stays with
   the rail: .rail-head's sizes + shrink steps. (The mobile homepage
   category's header uses the genre headings' .sec-title style
   instead — Kris, 2026-11-06.) */
@font-face {
  font-family: "Archivo Black";
  src: url("/assets/fonts/archivo-black.woff2") format("woff2"),
       url("/assets/fonts/archivo-black.ttf") format("truetype");
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}
.hof-brand-head {
  font-family: "Archivo Black", "Arial Black", Arial, sans-serif;
  font-weight: 900;
  text-transform: lowercase;
  letter-spacing: .01em;
  color: var(--gold);
  text-align: center;
  line-height: 1.1;
}
/* Mobile Hall of Fame category (Kris, 2026-11-06): homepage only,
   rendered at <=860px only — above that width the section is gone
   and desktop keeps its rail with no in-column HoF section. The
   section sits below New: its own HoF header banner
   (assets/hof-header.jpg, derived from the gold hall-master strip
   art), then a "Hall of Fame" header in the genre headings' own
   .sec-title text style but GOLD with a leading star (.hof-title —
   scoped to this header only; every other .sec-title stays
   off-white), then the standard .song cards in their gold HoF
   variant — no rail compact cards and no placeholder slots in
   this section. */
.hof-mobile { display: none; }
.hof-mobile .hof-title { color: var(--gold); border-left-color: var(--gold); }
@media (max-width: 860px) {
  .hof-mobile { display: block; }
}
@media (min-width: 1420px) {
  .wrap.has-cols { max-width: 1760px; }
  .cols {
    display: grid; grid-template-columns: 952px minmax(0, 1fr);
    column-gap: 40px; align-items: start;
  }
  .col-main { min-width: 0; }
  .col-rail {
    display: block; justify-self: end; width: 100%; max-width: 400px;
    /* Resting-top fix (Kris, 2026-11-06): the columns start at 85px
       (67px header + 18px wrap padding) but the sticky pin is 79px,
       so the rail visibly nudged UP 6px the moment scrolling began,
       closing part of the gap under the red line. The -6px top margin
       puts the rail's RESTING top exactly on its pin (79px): scroll
       start now produces zero apparent movement. The main column is
       untouched — the margin lives on the rail column only, and this
       one rule serves both rail bands (stacked + two-column). */
    margin-top: -6px;
    /* Sticky (Kris, 2026-11-06): the showcase stays in view while
       the main column scrolls past. The site header is itself sticky
       and 67px tall at this band, so the rail pins 12px below it
       (top: 79px) and its header can never slide under the header.
       The cap (viewport minus the pin offset and a 12px foot) plus
       internal scroll keep the bottom slots of the stacked rail
       (twenty slots, ~1900px at natural size) reachable at short
       viewports; the two-column rail stays under the cap at
       ordinary heights. */
    position: sticky; top: 79px;
    max-height: calc(100vh - 91px); overflow-y: auto;
  }
}
@media (min-width: 1500px) {
  /* Two-column rail (Kris, 2026-11-06; breakpoint lowered twice the
     same day — 1880px, then 1584px, then decisively ~1500px at
     Kris's word after his ~1840px window still rendered stacked).
     The sections are FLEXIBLE: the grid's 1fr/1fr split makes each
     section (rail width - 24px gap) / 2 — 228px at the 1500px
     breakpoint, ~270px at 1584px, ~398px at Kris's 1840px, and the
     400px cap once the rail reaches its 824px max-width. The frame
     math at the breakpoint is exact: 952 main (untouched) + 40
     column gap + 228 + 24 + 228 sections + 28 wrap padding =
     1500px, zero overflow. At the narrow end the action row wraps
     INSIDE its card (flex-wrap below) rather than spilling — every
     action stays present; at ~1700px and up the sections are wide
     enough that nothing ever wraps. The Hall of Fame brand head
     spans both columns; the section titles align at the tops of
     their columns. Below this band the 1760px cap and the single stacked
     rail are exactly as shipped. */
  .wrap.has-cols { max-width: 1908px; }
  .col-rail { max-width: 824px; }
  .hof-rail {
    display: grid; grid-template-columns: 1fr 1fr; column-gap: 24px;
  }
  .hof-rail .rail-head { grid-column: 1 / -1; }
  .rail-actions { flex-wrap: wrap; }
  /* Column swap (Kris, 2026-11-08): Featured Album + Discover on
     the LEFT, Top 10 in the Hall on the RIGHT — side-by-side band
     only; the stacked band keeps document order. */
  .rail-sec-disc { order: 1; }
  .rail-sec-top { order: 2; }
}
@media (min-width: 2100px) {
  /* Ultra-wide (Kris, 2026-11-10): with the rails up, the frame used
     to stop at 1908px and leave the rest of a wide monitor black.
     When the room is really there, the MAIN column takes it: the
     frame widens to 2130px, the main column grows 952px -> 1212px,
     and its song lists go 3-across (cards ~397px, the same width the
     no-rail 3-column band starts at). The rail keeps its 824px cap
     and its two sections exactly as designed. Frame math: 1212 main
     + 40 gap + 824 rail + 28 wrap padding = 2104 <= 2130. */
  .wrap.has-cols { max-width: 2130px; }
  .cols { grid-template-columns: 1212px minmax(0, 1fr); }
  .cols .song-list { grid-template-columns: 1fr 1fr 1fr; }
}

/* Rail fit steps (Kris, 2026-11-06): on narrower/shorter desktop
   screens the rail SCALES DOWN so both sections plus headers fit
   the visible screen height instead of clipping into the sticky
   cap's internal scroll (which remains only as a last-resort
   fallback — in the single-column band the stacked twenty slots
   exceed one screen at any step, and that band keeps the steps +
   internal scroll as accepted geometry). Every action stays
   present, just smaller. Steps: a two-column step (compact metrics,
   engages up to 1180px heights) guarantees all twenty slots + both
   headers fit the first screen at 1080px in the side-by-side band;
   step 1 (compact) covers ~900px-class laptop heights; step 2
   (mini) covers ~768px-class heights in both bands. */
@media (min-width: 1500px) and (max-height: 1180px) {
  .rail-head { font-size: 18px; }
  .rail-sec-title { font-size: 14px; margin-top: 2px; }
  .hof-rail { row-gap: 2px; }
  .rail-sec { gap: 2px; }
  .rail-card { padding: 2px 8px; }
  .rail-art { width: 58px; height: 58px; }
  .rail-song-title { font-size: 13.5px; }
  .rail-byline { font-size: 11px; margin-top: 0; }
  .rail-likes { font-size: 11px; margin-top: 0; }
  .rail-likes .hof-badge { font-size: 11px; }
  .rail-actions { margin-top: 1px; }
  .rail-actions .play-btn { width: 22px; height: 22px; font-size: 10px; }
  .rail-actions .btn { font-size: 10.5px; padding: 2px 6px; }
  .rail-actions .btn.like[data-like] { width: 100px; min-width: 100px; }
  .rail-ph, .rail-teaser { min-height: 70px; }
  .rail-ph-rank { flex-basis: 58px; font-size: 27px; }
  .rail-ph-text { font-size: 12px; }
}
@media (min-width: 1420px) and (max-height: 1070px) {
  .rail-head { font-size: 18px; }
  .rail-sec-title { font-size: 14px; margin-top: 2px; }
  .hof-rail { row-gap: 2px; }
  .rail-sec { gap: 2px; }
  .rail-card { padding: 2px 8px; }
  .rail-art { width: 58px; height: 58px; }
  .rail-song-title { font-size: 13.5px; }
  .rail-byline { font-size: 11px; margin-top: 0; }
  .rail-likes { font-size: 11px; margin-top: 0; }
  .rail-likes .hof-badge { font-size: 11px; }
  .rail-actions { margin-top: 1px; }
  .rail-actions .play-btn { width: 22px; height: 22px; font-size: 10px; }
  .rail-actions .btn { font-size: 10.5px; padding: 2px 6px; }
  .rail-actions .btn.like[data-like] { width: 100px; min-width: 100px; }
  .rail-ph, .rail-teaser { min-height: 70px; }
  .rail-ph-rank { flex-basis: 58px; font-size: 27px; }
  .rail-ph-text { font-size: 12px; }
}
@media (min-width: 1420px) and (max-height: 849px) {
  .rail-head { font-size: 15px; }
  .rail-sec-title { font-size: 13px; margin-top: 0; }
  .hof-rail { row-gap: 1px; }
  .rail-sec { gap: 1px; }
  .rail-card { padding: 2px 6px; gap: 8px; }
  .rail-art { width: 46px; height: 46px; }
  .rail-song-title { font-size: 12px; line-height: 1.05; }
  .rail-byline { font-size: 10.5px; line-height: 1.1; margin-top: 0; }
  .rail-likes { font-size: 10.5px; line-height: 1.1; margin-top: 0; }
  .rail-likes .hof-badge { font-size: 10.5px; }
  .rail-actions { margin-top: 0; }
  .rail-actions .play-btn { width: 19px; height: 19px; font-size: 9px; }
  .rail-actions .btn { font-size: 10px; padding: 1px 5px; }
  .rail-actions .btn.like[data-like] { width: 92px; min-width: 92px; }
  .rail-ph, .rail-teaser { min-height: 58px; }
  .rail-ph-rank { flex-basis: 46px; font-size: 22px; }
  .rail-ph-text { font-size: 11px; }
}

/* Featured Album heights for the rail fit steps (same alignment
   rule as the base 451px: this card's bottom meets the TOP 10
   column's #5 bottom, at each step's slot heights). The Featured
   Artist heights beside them follow their own rule (Kris,
   2026-11-10): the artist card's bottom meets the bottom of the
   Discover section — measured 450 full / 378 compact; the mini
   value steps down by the album cards' compact-to-mini delta. */
@media (min-width: 1420px) and (max-height: 1180px) {
  .rail-feature-card { height: 370px; }
  .rail-artist-card { height: 378px; }
}
@media (min-width: 1420px) and (max-height: 849px) {
  .rail-feature-card { height: 320px; }
  .rail-artist-card { height: 328px; }
}

/* Section pager (Kris, 2026-11-10): homepage sections show one
   page of 6 cards at a time. Circle arrows sit at the bottom
   left (previous) and bottom right (next) of the section; the
   visible 6 slide out in the paging direction and the next 6
   slide in to take their place (the .slide-* states below, driven
   from app.js). An arrow grays out (disabled) at its end of the
   category. Cards outside the current page carry .page-hidden. */
.song-list > .song.page-hidden { display: none; }
/* First paint matches the pager (Kris, 2026-11-10): the server
   only hides cards past the sixth, so at widths where a page is
   four (or two), the six still flashed before the script grouped
   them into panels — Kris caught it ("when it collapses to the
   4, the 6 still flashes"). Until the script builds the track,
   a paged list now shows only its first page at the current
   band: 2 cards at one column, 4 at two, 6 at three (the
   page-hidden cap above). Once the cards move into panels they
   are no longer direct children and these rules lapse. */
@media (max-width: 1239px) {
  .song-list[data-page]:not(.pager-viewport) > .song:nth-child(n+5) { display: none; }
}
@media (max-width: 860px) {
  .song-list[data-page]:not(.pager-viewport) > .song:nth-child(n+3) { display: none; }
}
@media (min-width: 1420px) and (max-width: 2099px) {
  .wrap.has-cols .song-list[data-page]:not(.pager-viewport) > .song:nth-child(n+5) { display: none; }
}
/* Section pager track (Kris, 2026-11-10, modeled on Spotify's
   shelf scroll): the section's cards sit in page panels on one
   horizontal track inside an overflow-hidden viewport, and
   paging translates the whole track by one panel — the old and
   new pages slide together in a single continuous motion, with
   no swap and no cleanup step. Panels mirror the song-list grid
   bands so a panel looks exactly like the grid page it replaces. */
.song-list.pager-viewport { display: block; overflow: hidden; }
.song-track { display: flex; gap: 10px; transition: transform .5s cubic-bezier(.22, .61, .36, 1); }
.song-track.no-anim { transition: none; }
.song-pg {
  flex: 0 0 auto; width: 100%; display: grid; gap: 8px;
  grid-template-columns: 1fr; align-content: start;
}
@media (min-width: 861px) {
  .song-pg { grid-template-columns: 1fr 1fr; gap: 10px; }
}
@media (min-width: 1240px) {
  .wrap:not(.has-cols) .song-pg { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 2100px) {
  .cols .song-pg { grid-template-columns: repeat(3, 1fr); }
}
@media (prefers-reduced-motion: reduce) {
  .song-track { transition: none; }
}
.pager-row {
  display: flex; justify-content: space-between; align-items: center;
  /* 20px above (Kris, 2026-11-10): at 12px the arrows crowded the
     last row of cards — a clear small gap now. */
  margin: 20px 0 4px;
}
/* Card action buttons at 3 columns (Kris, 2026-11-10): at three
   across, the card's action row is ~314px wide and the full-size
   buttons (Like 144 + Dislike ~73 + playlist 52 + Share 40 + gaps)
   overflow it, stranding the Share button on a second row. In the
   3-column bands the buttons shrink a step so all four stay on
   the row. Two bands qualify: the wide non-rail layout (>=1240px)
   and the rail layout's expanded main column (>=2100px). */
@media (min-width: 1240px) {
  .wrap:not(.has-cols) .song-actions { gap: 5px; }
  .wrap:not(.has-cols) .song-actions .btn { padding-left: 6px; padding-right: 6px; }
  .wrap:not(.has-cols) .song-actions .btn.like[data-like] { width: 140px; min-width: 140px; }
  .wrap:not(.has-cols) .song-actions .btn.share-btn { padding-left: 8px; padding-right: 8px; }
  .wrap:not(.has-cols) .song-actions select.add-pl { flex-basis: 44px; width: 44px; }
}
@media (min-width: 2100px) {
  .wrap.has-cols .song-actions { gap: 5px; }
  .wrap.has-cols .song-actions .btn { padding-left: 6px; padding-right: 6px; }
  .wrap.has-cols .song-actions .btn.like[data-like] { width: 140px; min-width: 140px; }
  .wrap.has-cols .song-actions .btn.share-btn { padding-left: 8px; padding-right: 8px; }
  .wrap.has-cols .song-actions select.add-pl { flex-basis: 44px; width: 44px; }
}
/* #1 HIT cards at desktop widths (Kris, 2026-11-10): the
   ribbon's 44px right padding narrows these cards' action row
   at EVERY width, but the squeeze existed only in the phone
   block — desktop has-hit cards wrapped their Share button
   onto a second line (seen ~900px and >=2100px). The same
   squeeze, applied at >=561px. */
@media (min-width: 561px) {
  .song.has-hit .song-actions { gap: 3px; }
  .song.has-hit .song-actions .btn { padding-left: 5px; padding-right: 5px; }
  .song.has-hit .song-actions select.add-pl { flex-basis: 44px; width: 44px; }
  /* One line, always (Kris, 2026-11-12): the >=561 action row
     wraps (flex-wrap: wrap from the 561 block), so on the #1 HIT
     card — whose row is 44px narrower for the ribbon — the Share
     button kept dropping to a second line whenever the row ran a
     few px wide (wider OS fonts, the playlist select, SUB ✓).
     The squeeze alone never cured it. Now: has-hit rows may NOT
     wrap, and the Support pin gives up its share of the ribbon's
     44px (128px — the HoF label is plain "♥ Support"/"Supported",
     no +24h, so it fits with headroom). */
  .song.has-hit .song-actions { flex-wrap: nowrap; }
  .song.has-hit .song-actions .btn.like[data-like] { width: 128px; min-width: 128px; }
}
/* Rail-page section growth (Kris, 2026-11-10): on rail pages the
   main column stretches to the rail's height. The last visible
   section (marked .sec-grow from app.js whenever the filter
   changes) grows to fill the column, and its bottom divider
   rides at the column's bottom — level with the bottom of the
   rail — while the pager arrows stay right under the cards. */
@media (min-width: 1420px) {
  /* height:100% because the .cols grid aligns items to start —
     without it the main column ends at its own content height,
     well above the rail's bottom. */
  .wrap.has-cols .col-main { display: flex; flex-direction: column; height: 100%; }
  .wrap.has-cols section.sec-grow { flex: 1 0 auto; display: flex; flex-direction: column; }
  /* The pager arrows ride just above the locked divider (Kris,
     2026-11-10): the slack goes above the arrows, not above the
     divider, so the arrows sit at the same height on every page —
     a short last page no longer leaves them floating midway. */
  .wrap.has-cols section.sec-grow .pager-row { margin-top: auto; margin-bottom: 0; }
  /* The same lock straight from CSS (Kris, 2026-11-10): the
     .sec-grow mark comes from a script, so on first paint the
     arrows sat up under the cards and then visibly shifted down
     when the script engaged the lock ("I can still see them
     trying to force shift downward"). The visible section IS
     the grow section — saying so in CSS puts the lock in the
     first frame, no script involved. */
  .wrap.has-cols .col-main > section[data-section]:not(.filtered-out) { flex: 1 0 auto; display: flex; flex-direction: column; }
  .wrap.has-cols .col-main > section[data-section]:not(.filtered-out) .pager-row { margin-top: auto; margin-bottom: 0; }
  /* With the dividers gone (Kris, 2026-11-10), the grow section's
     pager row is its last element: margin-bottom 0 puts the
     arrows' bottoms exactly on the rail's bottom line. */
}
/* Pager arrows, spruced (Kris, 2026-11-10): the plain text
   glyphs rendered thin and off-theme. Now drawn SVG arrows in a
   shaded dark ring that picks up the brand red on hover, echoing
   the round play button; the disabled end stays grayed, per the
   gray-out rule. */
.page-arrow {
  width: 39px; height: 39px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, #262626, #161616);
  color: #f2f2f2; border: 2px solid #3d3d3d;
  box-shadow: 0 3px 10px rgba(0, 0, 0, .55), inset 0 1px 0 rgba(255, 255, 255, .06);
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: border-color .18s ease, color .18s ease, transform .18s ease, box-shadow .18s ease;
}
.page-arrow svg { width: 18px; height: 18px; display: block; }
.page-arrow:hover:not(:disabled) {
  border-color: var(--red); color: #fff;
  box-shadow: 0 3px 14px rgba(0, 0, 0, .6), 0 0 0 3px rgba(229, 50, 45, .18);
  transform: scale(1.07);
}
.page-arrow:active:not(:disabled) { transform: scale(.96); }
.page-arrow:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }
.page-arrow:disabled {
  background: #121212; color: #5a5a5a; border-color: #242424;
  box-shadow: none; cursor: default;
}

/* ---- song comments (Kris, 2026-11-06) ----
   Below the song panel, above the below-card logo. Bylines are the
   commenter's NAME ONLY — in the brand red (Kris) — with the
   timestamp as a small dim element beside it; the message body is
   WHITE, never the dim tone. */
.song-comments { margin: 26px 0 4px; width: 100%; }
/* The composer spans the FULL width of the content column under the
   song panel (Kris, 2026-11-06), themed end to end: dark panel box,
   themed border, light text, dim placeholder, a brand-red focus
   ring, and the POST button in the red primary of the page's other
   actions. The counter stays a dim hint. */
.comment-form { margin: 12px 0 16px; width: 100%; }
.comment-form textarea {
  display: block; width: 100%; background: var(--panel); color: var(--txt);
  border: 1px solid var(--line); border-radius: 6px;
  padding: 10px 12px; font: inherit; resize: vertical;
}
.comment-form textarea::placeholder { color: var(--dim); }
.comment-form textarea:focus {
  border-color: var(--red); outline: none; box-shadow: 0 0 0 1px var(--red);
}
.comment-form button[type="submit"] {
  background: var(--red); border-color: var(--red); color: #fff;
}
.comment-form button[type="submit"]:hover {
  background: var(--red-dark); border-color: var(--red-dark);
}
.comment-form-row {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 8px;
}
.comment-count { color: var(--dim); font-size: 13px; }
.comment-list { display: flex; flex-direction: column; gap: 12px; }
.comment {
  background: var(--panel); border: 1px solid var(--line);
  border-left: 4px solid var(--red); border-radius: 6px; padding: 10px 14px;
}
.comment-head {
  margin: 0 0 4px; display: flex; align-items: baseline; gap: 10px;
  flex-wrap: wrap;
}
.comment-author { color: var(--red); font-weight: 700; }
.comment-time { color: var(--dim); font-size: 12px; }
.comment-del {
  margin-left: auto; background: none; border: 1px solid var(--line);
  color: var(--dim); border-radius: 3px; padding: 2px 8px; font-size: 12px;
  cursor: pointer; font: inherit; font-size: 12px;
}
.comment-del:hover { color: #fff; border-color: var(--red); }
.comment-body { margin: 0; color: #fff; }

/* Profile pictures (Kris, 2026-11-07): above the name, rounded corners;
   on the owner's own profile the picture itself is the button. */
.profile-pic { display: block; width: 120px; height: 120px; object-fit: cover; border-radius: 10px; margin: 4px 0 10px; background: #17171c; }
.avatar-btn { display: block; background: none; border: 0; padding: 0; margin: 0; cursor: pointer; }
.avatar-btn .profile-pic { margin-top: 0; }
@media (max-width: 560px) { .profile-pic { width: 96px; height: 96px; } }

/* Profile page — app-style cards (Kris, 2026-11-07: mirror the app Profile).
   Header row: picture left, IndieTime+ slot directly to its right. Every
   section is a dark rounded card with its title inside, red or gold. */
.profile-top { display: flex; gap: 16px; align-items: flex-start; margin-top: 6px; }
.profile-id { flex: 0 0 auto; max-width: 150px; }
.plus-slot { flex: 1 1 auto; min-width: 0; max-width: 470px; }
.plus-slot-card { background: var(--panel); border: 1px solid var(--line); border-radius: 14px; padding: 10px 14px; height: 120px; display: flex; flex-direction: column; justify-content: center; }
.plus-slot-head { display: flex; align-items: center; color: var(--gold); font-weight: 800; font-size: 19px; }
.plus-slot-head .plus-badge { height: 1.1em; width: 1.1em; }
.plus-slot-copy { color: var(--dim); font-size: 13px; line-height: 1.3; margin: 4px 0 0; }
.plus-slot-btns { display: flex; gap: 8px; margin-top: 9px; }
.plus-slot-btns .btn { flex: 1 1 0; min-width: 0; text-align: center; padding: 7px 4px; font-size: 13.5px; }
.lbl-short { display: none; }
.plus-slot-card.plus-on { padding: 8px 10px; }
.plus-logo-img { display: block; width: 100%; flex: 1 1 auto; min-height: 0; object-fit: contain; }
.plus-slot-status { color: var(--dim); font-size: 11.5px; line-height: 1.32; text-align: center; margin: 4px 0 0; }
.plus-slot-note { font-size: 10px; opacity: .8; }
.page-title.profile-name { color: var(--red); text-transform: none; letter-spacing: 0; border-bottom: 0; padding-bottom: 0; margin-top: 12px; }
/* The name on your own profile is the edit door (Kris, 2026-11-08):
   it links to Account settings, wearing the heading's own color. */
.profile-name-link { color: inherit; text-decoration: none; cursor: pointer; }
.profile-name-link:hover { text-decoration: underline; }
.profile-email { color: var(--dim); margin: 2px 0 0; font-size: 16px; }
.pl-title { display: flex; align-items: center; gap: 6px; }
.pl-rename { cursor: pointer; }
.pl-list a.pl-open { color: inherit; text-decoration: none; }
.pl-title-trash { background: none; border: none; cursor: pointer; font-size: 17px; line-height: 1; padding: 4px 2px; }
.profile-actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 12px 0 4px; }
.profile-actions .btn { flex: 1 1 0; text-align: center; }
.profile-card { background: var(--panel); border: 1px solid var(--line); border-radius: 14px; padding: 18px; margin: 20px 0; line-height: 1.35; }
.profile-card.earnings-card { border-left: 1px solid var(--line); border-radius: 14px; max-width: none; }
.card-title { color: var(--red); font-size: 21px; font-weight: 800; margin: 0 0 4px; letter-spacing: .01em; }
.card-title.gold { color: var(--gold); }
.card-title .sec-sub { font-size: 13px; color: var(--dim); font-weight: 400; margin-left: 8px; }
.profile-card h3 { color: #fff; text-transform: uppercase; letter-spacing: .04em; margin: 9px 0 4px; }
/* Tighter spacing between text lines inside profile cards (Kris, 2026-11-07). */
.profile-card p { margin: 3px 0; }
.profile-card .pl-list { margin: 2px 0; }
.profile-card .pl-list li:last-child { border-bottom: 0; }
.verify-banner { border-radius: 14px; }
@media (max-width: 560px) {
  .plus-slot-card { height: 96px; padding: 8px 10px; }
  .plus-slot-card.plus-on { padding: 6px 10px; }
  .plus-slot-card.plus-on .plus-slot-status { margin-top: 2px; }
  .plus-slot-head { font-size: 17px; }
  .plus-slot-copy { font-size: 12px; }
  .plus-slot-btns { margin-top: 7px; }
  .plus-slot-btns .btn { font-size: 12px; padding: 6px 2px; }
  .lbl-full { display: none; }
  .lbl-short { display: inline; }
  .profile-card { padding: 15px; }
}

/* Playlist + subscription NAMES read white, not red (Kris, 2026-11-07);
   red stays for headings and actions. Profile subscription rows carry a
   trash button (left) that unsubscribes in place via app.js. */
.name-link { color: #fff; }
.sub-row { display: flex; align-items: center; gap: 8px; }
.sub-trash { padding: 4px 7px; line-height: 0; flex: 0 0 auto; }

/* Playlists page rows (Kris, 2026-11-10): the name, count and a
   tiny trash ride one flex bar; the boxless gray trash sits at
   the far right — deleting the playlist or unsubscribing the
   artist (one at a time; the All subscriptions row has none). */
.pl-list.big li { display: flex; align-items: center; gap: 8px; }
.pl-list .row-trash { margin-left: auto; padding: 4px 6px; line-height: 0; flex: 0 0 auto; background: none; border: none; box-shadow: none; color: #9a9a9a; cursor: pointer; }
.pl-list .row-trash:hover { color: #c9c9c9; }
/* Your uploads delete trash (Kris, 2026-11-10): the same gray borderless
   treatment inside the song card's actions row, pushed to the far right. */
.song-actions .row-trash { margin-left: auto; padding: 4px 6px; line-height: 0; flex: 0 0 auto; background: none; border: none; box-shadow: none; color: #9a9a9a; cursor: pointer; }
.song-actions .row-trash:hover { color: #c9c9c9; }

/* Live reorder flash (Kris, 2026-11-09): a row or card that just
   moved up or down a spot in its chart flashes white. */
@keyframes rowFlash {
    0% { background-color: rgba(255, 255, 255, 0.92); }
    100% { background-color: rgba(255, 255, 255, 0); }
}
.row-flash { animation: rowFlash 0.55s ease-out 2; }

/* Hall of Fame bar (Kris, 2026-11-10): its buttons are GOLD,
   not red — gold outline at rest, gold fill when active — and
   the Back button at the head of the bar matches them. */
.hall-tools .gbtn { border-color: var(--gold); }
.hall-tools .gbtn:hover { border-color: #ffe9a8; }
.hall-tools .gbtn.active { background: linear-gradient(180deg, #ffd968, var(--gold) 55%, #d9a521); border-color: #ffe9a8; }

.hall-tools .gbtn.active span { color: #191919; }

/* Hall genre headers run gold (Kris, 2026-11-10): on the Hall
   page only, each genre section's header art — the band strip,
   and the same art washed through its feature card below
   1420px where the band hides — shifts from the brand red to
   gold. The landing and genre pages keep the red art; the
   "In the Hall" section keeps its own (already gold-toned) art. */
section.hall-genre[data-section^="g"] .strip-band .strip,
section.hall-genre[data-section^="g"] .sec-feature::before {
  filter: hue-rotate(42deg) saturate(1.25) brightness(1.04);
}

/* The "In the Hall" band matches the genre bands' gold (Kris,
   2026-11-10): its own art is a paler tan-gold that read reddish
   next to the shifted genre art — deepen its saturation to the
   same vivid amber. */
section.hall-genre[data-section="all"] .strip-band .strip,
section.hall-genre[data-section="all"] .sec-feature::before {
  filter: saturate(1.7) hue-rotate(-10deg) brightness(0.97);
}
.back-btn {
  transform: skewX(-14deg); flex: 0 0 auto; cursor: pointer;
  background: linear-gradient(180deg, #1b1b1b, #060606);
  border: 2px solid var(--red); border-radius: 9px; padding: 9px 13px;
  text-decoration: none; line-height: normal;
}
.back-btn span {
  display: block; transform: skewX(14deg); color: #fff; line-height: normal;
  font-family: "Archivo Black", "Arial Black", Arial, sans-serif;
  font-size: 13px; letter-spacing: .02em; white-space: nowrap;
}
.back-btn:hover { border-color: #ff6b6b; text-decoration: none; }
/* Back is simply the first button on the bar (Kris, 2026-11-10):
   it lives inside .genre-bar, takes the bar's own gaps, and on
   desktop grows to fill like every other bar button. */
@media (min-width: 1024px) {
  .hall-tools .back-btn { flex: 1 1 auto; text-align: center; }
}
@media (min-width: 1024px) {
  .back-btn { padding: 9px 8px; }
  .back-btn span { font-size: 12px; }
}

/* Landing genre bar (Kris, 2026-11-10): a gold "Hall" button at
   the far right end navigates to the Hall of Fame. It is a link,
   not a filter — its own class keeps it out of the .gbtn
   machinery — but it wears the bar's exact metrics. */
.hall-btn {
  transform: skewX(-14deg); flex: 0 0 auto; cursor: pointer;
  background: linear-gradient(180deg, #1b1b1b, #060606);
  border: 2px solid var(--gold); border-radius: 9px; padding: 9px 13px;
  text-decoration: none; line-height: normal;
}
.hall-btn span {
  display: block; transform: skewX(14deg); color: #fff; line-height: normal;
  font-family: "Archivo Black", "Arial Black", Arial, sans-serif;
  font-size: 13px; letter-spacing: .02em; white-space: nowrap;
}
.hall-btn:hover { border-color: #ffe9a8; text-decoration: none; }
@media (min-width: 1024px) {
  .hall-btn { padding: 9px 8px; flex: 1 1 auto; text-align: center; }
  .hall-btn span { font-size: 12px; }
}

/* Hall of Fame (Kris, 2026-11-10): the star title sits centered
   between the pager arrows under the songs — the two arrows are
   equal width, so the middle child lands perfectly centered. */
/* Mobile web (Kris, 2026-11-10): the pager arrows give way to
   swiping — the arrows hide at phone widths (the swipe handler
   lives with the pager code in app.js) and a lone pager title
   centers in the row the arrows left. */
@media (max-width: 1023px) {
  .page-arrow { display: none; }
  .pager-row { justify-content: center; }
}
.pager-row .pager-title { font-family: "Archivo Black", "Arial Black", Arial, sans-serif; color: var(--gold); font-size: clamp(14.4px, 4.8vw, 38.4px); letter-spacing: .03em; align-self: center; white-space: nowrap; }

/* ---- Support show (Kris, 2026-11-12): burst particles, absorb ring,
   countdown pulse, and the "Supported" fade-in. JS drives the flight;
   reduced-motion users never see these (the JS takes an instant path). */
.support-shard { position: fixed; z-index: 10001; margin: 0; pointer-events: none; }
.support-ring { position: fixed; z-index: 10000; border: 2px solid #f5b921; border-radius: 50%; pointer-events: none; }
.btn.like.support-ghost { opacity: 0; }
.btn.like.support-arrive { animation: supportArrive .6s ease both; }
@keyframes supportArrive { from { opacity: 0; transform: scale(.88); } to { opacity: 1; transform: scale(1); } }
[data-countdown].support-pulse { display: inline-block; animation: supportPulse .55s ease; }
@keyframes supportPulse { 0% { transform: scale(1); } 35% { transform: scale(1.14); } 100% { transform: scale(1); } }
@media (prefers-reduced-motion: reduce) {
  .btn.like.support-arrive, [data-countdown].support-pulse { animation: none; }
}

/* Visualizer options on the profile page (2026-11-12) */
.viz-opts { display: grid; gap: 8px; margin: 10px 0 14px; }
.viz-opts label { display: flex; gap: 10px; align-items: flex-start; background: rgba(255,255,255,.03); border: 1px solid var(--line); border-radius: 6px; padding: 10px 12px; cursor: pointer; }
.viz-opts input[type="radio"] { accent-color: var(--red); margin-top: 3px; flex: 0 0 auto; }

/* IndieArcade (2026-11-12): header ship button + the homepage arena */
.head-arcade { display: inline-flex; align-items: center; justify-content: center; width: 38px; height: 38px; color: #fff; background: none; border: 1px solid var(--line); border-radius: 6px; flex: 0 0 auto; cursor: pointer; padding: 0;  margin-left: 6px; }
.head-arcade:hover { color: var(--red); }
.head-arcade.on { color: var(--red); border-color: var(--red); }
@media (max-width: 700px) {
  /* One-line mobile header (Kris, 2026-11-12): with three game
     buttons + search + hamburger next to the logo, the old
     18px gaps and 44px logo wrapped the hamburger to a second
     row. Everything shrinks and the gaps close so the whole bar
     stays on one line; the nav dropdown still drops below when
     opened (flex-wrap stays on). */
  .site-head { gap: 6px; padding: 8px 10px; }
  .brand img { height: 30px; }
  .mature-badge { height: 30px; }
  .nav-toggle { font-size: 19px; padding: 5px 9px; }
  .head-search { order: 2; margin-left: 0; width: 30px; height: 30px; }
  .head-arcade { order: 2; margin-left: 4px; width: 30px; height: 30px; }
  #arcade-btn { margin-left: auto; }
}
.hero, .home-tools { transition: opacity .45s ease, visibility .45s ease; }
main.arcade-on .hero, main.arcade-on .home-tools { opacity: 0; visibility: hidden; }
#arcade-stage { position: absolute; z-index: 30; border: 1px solid var(--line); border-radius: 8px; background: rgba(4,4,6,.55); overflow: hidden; opacity: 0; transition: opacity .45s ease; }
#arcade-stage.on { opacity: 1; }
#arcade-canvas { display: block; width: 100%; height: 100%; touch-action: none; }
.arcade-overlay { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 9px; text-align: center; padding: 10px; pointer-events: none; }
.arcade-overlay .arc-title { font-size: clamp(34px, 7vw, 60px); font-weight: 900; letter-spacing: 1px; color: #fff; text-shadow: 0 2px 18px rgba(232,35,42,.45); line-height: 1; }
.arcade-overlay .arc-title .arc-red { color: var(--red); }
.arcade-overlay .arc-count { font-size: clamp(52px, 11vw, 88px); font-weight: 900; color: #fff; text-shadow: 0 2px 22px rgba(232,35,42,.6); line-height: 1; }
.arcade-overlay .arc-sub { color: var(--dim); font-size: 13.5px; }
.arcade-overlay .arc-over { font-size: clamp(26px, 5vw, 40px); font-weight: 900; color: #fff; letter-spacing: 3px; line-height: 1; }
.arcade-overlay .arc-score { font-size: clamp(18px, 3.4vw, 25px); color: #fff; }
.arcade-overlay .arc-score b { color: var(--gold); }
.arcade-board { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }
.arcade-chip { display: inline-flex; align-items: center; gap: 8px; background: rgba(17,17,17,.5); border: 1px solid var(--line); border-radius: 999px; padding: 4px 12px 4px 4px; color: #fff; font-size: 13px; }
.arcade-chip.me { border-color: var(--gold); }
.arcade-chip img { width: 30px; height: 30px; border-radius: 50%; object-fit: cover; display: block; }
.arcade-chip .arc-chip-score { color: var(--gold); font-weight: 800; }
/* Game-over board at 50% size (Kris, 2026-11-12). */
.arcade-board.small { gap: 4px; }
.arcade-board.small .arcade-chip { gap: 4px; padding: 2px 6px 2px 2px; font-size: 10.5px; }
.arcade-board.small .arcade-chip img { width: 15px; height: 15px; }
.arcade-btns { display: flex; gap: 10px; pointer-events: auto; }
/* Arcade ad break (Kris, 2026-11-12): the IndieTime+ card takes
   the arena between games for non-Plus members. */
.arc-ad { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; gap: 14px; padding: 12px; box-sizing: border-box; background: rgba(3,3,5,.8); pointer-events: auto; }
.arc-ad-top { height: 100%; max-height: 200px; width: auto; border-radius: 10px; box-shadow: 0 6px 24px rgba(0,0,0,.5); }
.arc-ad-btns { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.arc-ad-btn img { height: 50px; width: auto; display: block; border-radius: 8px; }
.arc-ad-chip { position: absolute; top: 8px; right: 10px; background: rgba(0,0,0,.7); border: 1px solid rgba(255,255,255,.3); color: #fff; font-size: 12px; font-weight: 700; padding: 4px 11px; border-radius: 999px; z-index: 3; }
.arc-ad-chip b { color: #ffb347; }
@media (max-width: 560px) { .arc-ad { gap: 10px; padding: 10px; } .arc-ad-top { max-height: 172px; } .arc-ad-btn img { height: 43px; } }
